shell脚本基础之详解基本脚本的构建

Posted 昱Wy

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了shell脚本基础之详解基本脚本的构建相关的知识,希望对你有一定的参考价值。

基本脚本的构建

重定向

  • 有些时候我们需要保存某个命令的输出而不仅仅只是让它显示在标准输出显示器上。bash shell提供了几个操作符,可以将命令的输出重定向到另一个位置(比如文件等等)
  • 重定向不仅可以用于输入,还可以用于输出,将文件重定向到命令输入

输出重定向

覆盖

  • 最基本的重定向就是将命令的输出保存到一个文件中。bash shell使用大于号操作符(>)来实现此操作的,命令格式如下:
command > outputfile...
  • 将命令(command)的输出保存到指定的输出文件中(outputfile),如下:
date > test6

  • 重定向操作符创建了一个文件test6(通过默认的umask设置权限),并将命令date的输出重定向到test6这个指定的输出文件中
  • 如果指定的输出文件存在,重定向操作符会使用新的文件覆盖已有的文件,如下:
who > test6

  • 上述的test6文件内容已经不是第一次date命令的输出了,而是第二次who命令的输出

追加

  • 但是有时我们并不希望覆盖掉文件原有的内容,而只是想将当前命令的输出追加到已有的文件中,比如正在创建一个记录系统的某个服务的操作日志文件。在此情况下,可以使用双大于号(>>)来进行追加数据,如下:
date >> test6

  • 如上所示,特定输出文件test6中不仅包含更早的who命令的输出数据,还包括刚刚操作date命令的输出

输入重定向

  • 输入重定向和上述的输出重定向正好相反。输入重定向将文件的内容重定向到命令行,而非将命令行的输出重定向到文件
  • 输入重定向操作符号小于号(<),如下:
command < inputfile...
  • 可以将两种重定向对比进行记忆:在命令行上,命令总是在第一个(即为左侧),而重定向操作符“指向”数据流动的方向,小于号(<)就代表数据就是从输入文件流向当前命令,如下:
  • 举例:wc命令使用输入重定向
wc < test6

  • wc命令可以对数据文本进行计数,默认情况,此命令会输出3个值
    • 文本的行数
    • 文本的词数
    • 文本的字节数
  • 通过将test6文本文件重定向到wc命令,得到文本文件中的行、词和字节的计数,与ls -l 命令输出对比查看

内联输入重定向

  • 与文件输入重定向不同,还有一种输入重定向的方法,被称为内联输入重定向(inline input redirection),这种方法并不需要使用文件进行重定向,只需要在命令行中指定用于输入重定向的数据即可,乍看起来,怪,很怪!!但是有的时候确实需要用到这种方式
  • 内联输入重定向操作符为远小于号(<<),除了这个符号,你还必须指定一个文本标记来划分输入数据的开始与结尾,任何字符串都可以作为文本标记,但是数据的开始和结尾文本标记必须相同!!,格式如下:
command << marker 
data
....
.... 
marker
  • 如下测试:
wc << WY
....
....
> WY

  • 在命令行使用上使用内联输入重定向时,shell会用PS2环境变量中定义的次提示符(>),来提示输入数据,次提示符会持续提示,以获取更多用户输入的数据,直到你输入了作为文本标记的那个特殊的字符串(上述为:WY),wc命令才会对内联输入重定向提供的数据进行统计

管道

  • 有时,我们需要将一个命令的数据作为另一个命令的输入,可以使用上述的重定向知识来实现,只是显得操作繁琐,如下:
pip list > pip.list
sort < pip.list

  • pip命令是python的包管理器,使用pip list命令可以展示当前已安装的python包目录列表,但是这个列表并不会遵循某个特定的顺序,如果你要查找某个你已经安装的包的版本,直接在输出查找显得不是很方便
  • 通过标准输出重定向,pip命令的输出被重定向到了文件 pip.list。命令完成后,pip.list保存着系统中所有已安装的python软件包列表。接下来,输入重定向将pip.list文件的内容发送给sort命令, 该命令按字母顺序对python软件包名称进行排序,然后展示
  • 上述方法确实是管用的,但是操作步骤仍然比较繁琐,我们其实是不需要将命令输出重定向到文件中,然后再将此文件输入重定向到另一个命令,可以直接将命令的输出重定向到另一个命令,这个过程叫做管道连接(piping)
  • 和命令替换所使用的反引号(`)一样,管道符号在shell编程之外是很少用到的,在美式键盘上,它通常和反斜线(\\)位于同一个键。管道符(|)被放在命令之间,将一个命令的输出重定向到另一个命令中,如下:
command | command
  • 由管道串起的两个命令不是依次执行。Linux系统实际上会同时运行这两个命令,在系统内部将它们连接起来。在第一个命令产生输出的同时,输出会被立即送给第二个命令。数据传输不会用到任何中间文件或缓冲区
  • 现在可以将上述的重定向操作修改为管道操作,如下:
pip list | sort

  • 由于管道操作是实时运行的,所以只要pip命令一输出数据,sort命令就会立即对其进行排序。等到pip命令输出完数据,sort命令就已经将数据排好序并展示在标准输出显示器上
  • 还可以在一条命令中使用任意多条管道。可以持续地将命令的输出通过管道传给其他命令来细化操作
  • 可以将上述已经排序好的输出,使用文本分页命令(例如less或more)来强制将输出按屏显示,如下:
pip list | sort | more

  • 这行命令序列先执行pip 命令,将它的输出通过管道传给sort命令,然后再将sort排序好的输出通过管道传给more命令显示,在显示满一个屏幕后停止,使得我们可以在继续处理之前来浏览屏幕上已经输出的信息,如上图所示
  • 我们还可以搭配管道与重定向,将排序好的数据保存到文件中,如下:
pip list | sort > sort.list
more sort.list

  • 时下,管道最流行的用法之一是将命令产生的大量输出通过管道传递给more或less命令,这对ls命令是尤为常见的,如下:
ls -l | more

  • ls -l 会输出当前目录中的所有文件的长列表,对于包含大量文件的目录来说,这个展示到屏幕的列表会相当长,可以通过将输出管道连接到more命令,强制将输出展示到一个屏幕后停止下来

执行数学运算

对于任何一个编程语言来说都有着一个重要特性就是操作数字,进行各种运算的能力,但是,对于shell脚本来说,这个处理过程比较麻烦,在shell脚本中有两个途径可以进行数学运算

expr命令

  • 最开始,Bourne shell提供了一个特别的命令用来处理数学表达式。expr命令允许在命令行上处理数学表达式,但是特别笨拙,如下:
expr 6 + 6

  • 上述的数字与符号之间必须加空格,否则会出现奇怪的错误!!!如下:
  • expr命令还可以识别少数的数字与字符串操作符,如下表
操作符详细描述
ARG1 | ARG2如果ARG1既不是null也不是零值,返回ARG1;否则返回ARG2
ARG1 & ARG2如果没有参数是null或零值,返回ARG1;否则返回0
ARG1 < ARG2如果ARG1小于ARG2,返回1;否则返回0
ARG1 <= ARG2如果ARG1小于或等于ARG2,返回1;否则返回0
ARG1 = ARG2如果ARG1等于ARG2,返回1;否则返回0
ARG1 != ARG2如果ARG1不等于ARG2,返回1;否则返回0
ARG1 >= ARG2如果ARG1大于或等于ARG2,返回1;否则返回0
ARG1 > ARG2如果ARG1大于ARG2,返回1;否则返回0
ARG1 + ARG2返回ARG1和ARG2的算术运算和
ARG1 - ARG2返回ARG1和ARG2的算术运算差
ARG1 * ARG2返回ARG1和ARG2的算术乘积
ARG1 / ARG2返回ARG1被ARG2除的算术商
ARG1 % ARG2返回ARG1被ARG2除的算术余数
STRING : REGEXP如果REGEXP匹配到了STRING中的某个模式,返回该模式匹配
match STRING REGEXP如果REGEXP匹配到了STRING中的某个模式,返回该模式匹配
substr STRING POS LENGTH返回起始位置为POS(从1开始计数)、长度为LENGTH个字符的子字符串
index STRING CHARS返回在STRING中找到CHARS字符串的位置;否则,返回0
length STRING返回字符串STRING的数值长度
+ TOKEN将TOKEN解释成字符串,即使是个关键字
(EXPRESSION)返回EXPRESSION的值
  • 尽管标准操作符在expr命令中工作得很好,但在脚本或命令行上使用它们时仍有问题出现。许多expr命令操作符在shell中另有含义(比如星号)。当它们出现在在expr命令中时,会得到一些奇奇怪怪的结果,如下:
    -要解决上述问题,对于那些容易被shell错误解释的字符,在它们传入expr命令之前,需要使用shell的转义字符(反斜线)将其标出来,如下:
  • 在命令行上使用expr已经如此麻烦了,在shell脚本中使用expr命令却更为复杂!!
#!/bin/bash
# 使用expr进行计算
val1=10
val2=60
val3=$(expr $val2 / $val1)
echo 计算结果为:$val3

  • 上述,要将一个数学算式的结果赋给一个变量,需要使用命令替换来获取expr命令的输出,还是比较麻烦的,但是bash shell针对处理数学运算符有所改进

使用方括号

  • bash shell为了保持跟Bourne shell的兼容而包含了expr命令,但它同样也提供了一种更简单的方法来执行数学表达式。在bash shell中,在将一个数学运算结果赋给某个变量时,可以用美元符和方括号($[ operation ])将数学表达式围起来,如下:
  • 用方括号执行shell数学运算比用expr命令方便很多,在shell脚本中也可以实现,如下:
#!/bin/bash
var1=10
var2=50
var3=2
var4=$[$var1 * ($var2 + $var3)]
echo 计算结果为:$var4

  • 同样,注意在使用方括号来计算公式时,不用担心shell会误解乘号或其他符号。shell知道它不是通配符,因为它在方括号内
  • 在bash shell脚本中进行算术运算还会有一个主要的限制,如下:
#!/bin/bash
var1=100
var2=45
var3=$[$var1 / $var2]
echo 计算结果为:$var3

  • bash shell数学运算符只支持整数运算。若要进行任何实际的数学计算,还需要使用其它的操作

浮点数解决方案

有几种解决方案能够克服bash中数学运算的整数限制。最常见的方案是用内建的bash计算器,叫作bc

bc的基本用法

  • bash计算器实际上是一种编程语言,它允许在命令行中输入浮点表达式,然后解释并计算该表达式,最后返回结果。bash计算器能够识别:
    • 数字(整数和浮点数)
    • 变量(简单变量和数组)
    • 注释(以#或C语言中的/* */开始的行)
    • 表达式
    • 编程语句(例如if-then语句)
    • 函数
  • 可以在命令行中输入bc命令来访问bash计算器,如下:
  • 这个例子输入了表达式52.1 * 10,bash计算器返回了计算结果。随后每个输入到计算器的表达式都会被求值并显示出结果。要退出bash计算器,你必须输入quit退出命令
  • 浮点运算保留的小数位数是由内建变量scale控制的。必须将这个值设置为你希望在计算结果中保留的小数位数,否则可能无法得到期望的结果,如下:
  • scale变量的默认值是0。注意:scale变量无法限制浮点数的乘法运算,在scale值被设置前,bash计算器的计算结果不包含小数位。在将其值设置成4后,bash计算器显示的结果包含四位小数。-q 选项可以不显示bash计算器冗长的欢迎信息
  • 除了普通的数字计算,bc计算器还支持变量,如下:
  • 变量一旦被定义,就可以在整个bash计算器会话中使用该变量了。print语句允许你打印展示变量和数字

在shell脚本中使用bc

  • 你可能想问如何在shell脚本中使用bc来处理浮点运算的,还记得命令替换吗?是的,可以用命令替换运行bc命令,并将输出赋给一个变量。如下格式:
var=$(echo "options1; options2;... expression" | bc)
  • options参数允许你设置多个变量。如果你需要不止一个变量,可以用分号将其分开。expression参数定义了通过bc执行的数学表达式,如下:
#!/bin/bash
var1=$(echo "scale=4; w=3.14; y=66; w / y" | bc)
echo 计算结果为:$var1

  • 上述例子,将scale变量设置成了四位小数,设置了多个options参数,并在expression部分指定了特定的运算
  • 还可以使用脚本中定义的变量,它们都可以用在expression部分,然后发送给bc命令,如下:
#!/bin/bash
var1=22
var2=3.14159265354
var3=$(echo "scale=4; $var1 * $var1" | bc)
var4=$(echo "scale=4; $var2 * $var3" | bc)
echo 圆的面积为:$var4

  • 上述方法适用于较短的运算,但有时你会涉及更多的数字。当我们需要进行大量运算,在一个命令行中列出多个表达式就会有点麻烦
  • 还有一个方法可以解决这个问题。bc命令能识别输入重定向,允许你将一个文件重定向到bc命令来处理。但这同样会令人头疼,因为还得将表达式存放到文件中
  • 最好的办法是使用内联输入重定向,它允许你直接在命令行中重定向数据。在shell脚本中,你可以将输出赋给一个变量,如下格式:
var=$(bc << EOF 
option1
option2
.....
statements 
expressions 
EOF 
)
  • EOF文本字符串标识了内联重定向数据的起止。记住,仍然需要命令替换符号将bc命令的输出赋给变量,如下:
#!/bin/bash
var1=11.1
var2=22.2
var3=33.3
var4=44.4
var5=$(bc << EOF
scale=4
a1=($var1 + $var2)
b1=($var3 * $var4)
a1 + b1
EOF
)
echo 计算结果为:$var5

  • 选项和表达式放在shell脚本的不同行中可以让处理过程变得更清晰,提高易读性。EOF字符串标识了重定向给bc命令的数据的起止。当然,必须用命令替换符号标识出用来给变量赋值的命令
  • 可能注意到,在上述例子中,我们可以在bash计算器中赋值给变量。这一点很重要:在bash计算器中创建的变量只在bash计算器中有效,不能在shell脚本中使用

退出脚本

  • 到目前为止所有的示例脚本中,shell脚本都是突然停下来的。运行完最后一条命令时,脚本就结束了。当然还有另外一种更优雅的方法可以为脚本划上一个完美句号
  • shell中运行的每个命令都使用退出状态码(exit status)告诉shell它已经运行完毕。退出状态码是一个0~255的整数值,在命令结束运行时由命令传给shell。可以捕获这个值并在脚本中使用

查看退出状态码

  • Linux提供了一个专门的变量\\$?来保存上个已执行命令的退出状态码。对于需要进行检查的命令,必须在其运行完毕后立刻查看或使用$?变量。它的值会变成由shell所执行的最后一条命令的退出状态码,如下:
  • 按照惯例,一个成功结束的命令的退出状态码是0。如果一个命令结束时有错误,退出状态码应该是一个正数值,如下:
  • 无效命令会返回一个退出状态码127。Linux错误退出状态码没有固定标准,但有一些可用的参考,如下表:
状态码描 述
0命令成功结束
1一般性未知错误
2不适合的shell命令
126命令不可执行
127没找到命令
128无效的退出参数
128+x与Linux信号x相关的严重错误
130通过Ctrl+C终止的命令
255正常范围之外的退出状态码
  • 退出状态码126表明用户没有执行命令的权限,如下:
  • 还有一个常见的错误就是给了某个命令无效的参数,这会产生一般性的退出状态码1,表明在命令中发生了未知错误,如下:

exit命令

  • 默认情况下,shell脚本会以脚本中的最后一个命令的退出状态码退出,如下:
  • 可以改变这种默认行为,返回自定义的退出状态码。exit命令允许你在脚本结束时指定一个退出状态码,如下:
#!/bin/bash
var1=22
var2=3.14159265354
var3=$(echo "scale=4; $var1 * $var1" | bc)
var4=$(echo "scale=4; $var2 * $var3" | bc)
echo 圆的面积为:$var4
exit 5
  • 当查看脚本的退出码时,会得到作为参数传给exit命令的值,如下
  • 甚至你还可以在exit命令的参数中使用变量,如下:
#!/bin/bash
var1=44
var2=22
exit $[$var1 + $var2]

  • 还要注意一点,退出状态码最大只能是255,当我们自定义的退出状态码大于255时,会发生什么状况呢??如下:
  • 退出状态码的区间为0~255。shell通过模运算得到这个结果。一个值的模就是被除后的余数。最终的结果是指定的数值除以256后得到的余数。在上述的例子中,指定的值是521(返回值),余数是9,因此这个余数就成了最终的状态退出码
  • 在后续的学习中我们会了解如何用if-then语句来检查某个命令返回的错误状态,以便知道命令是否成功执行

shell脚本基础知识详解

shell脚本基础知识

1、什么是shell

shell是外壳的意思,就是操作系统的外壳。我们可以通过shell命令来操作和控制操作系统,比如Linux中的shell命令就包括ls、cd、pwd等等。总结来说,shell是一个命令解释器,它通过接受用户输入的Shell命令来启动、暂停、停止程序的运行或对计算机进行控制。

2、shell脚本的意义

shell脚本与Windows/Dos下的批处理相似,也就是将各类命令预先放入到一个文件中方便一次性执行,这个程序文件就叫做shell脚本。shell脚本可以记录命令执行的过程和执行逻辑,以便以后重复执行,还可以批量、定时处理主机,方便管理员进行设置或者管理。它比Windows下的批处理更强大,比用其他编程语言编写程序的效率更高。

3、如何创建shell脚本

在创建shell脚本时,通常会新建以.sh或.script结尾的文件,但shell脚本不一定要以.sh结尾,以.sh结尾只是为了拿到该文件的人能够快速识别该文件为脚本,即命名规范。创建脚本后在进一步写入命令前,我们需要写入幻数#!/bin/bash#!/bin/bash可以理解为脚本文件中所有命令运行的环境。

实验步骤:
1)切换到一个空目录/mnt中,新建以.sh结尾的脚本文件,通常我们需要在进一步写入命令前写入#!/bin/bash和脚本首部信息
在这里插入图片描述
2)在写脚本首部信息时,为了避免每次手动输入,可以编写vim的子配置文件~/.vimrc(只针对脚本编写者自己生效)设置自动添加脚本首部信息的函数:func为函数类型标志;endfunc为函数结束标志;call表示调用;append表示添加语句。在append添加语句中,0表示在第一行添加,所要添加的内容需要用 “ ” 引起来;当需要添加多个内容时,可以用 . 隔开,strftime表示自动识别捕捉时间并以年/月/日格式输出显示。自动添加脚本首部信息的函数编写完成后,我们在编写脚本时可以通过以下两种方式调用该函数:
方法一: map设定按键调用函数,其中’s是固定格式
在这里插入图片描述
此时创建脚本后按设定的按键()即可调用自动添加脚本首部信息的函数WESTOS()
在这里插入图片描述
方法二: autocmd设定当新建以.sh或.script结尾的文件时自动调用函数
在这里插入图片描述
此时新建以.sh或.script结尾的文件时会自动调用添加脚本首部信息的函数WESTOS()
在这里插入图片描述

4、如何执行shell脚本

实验步骤:
1)创建并编写脚本westos.sh,写入命令(cal命令表示显示当前月的日历)
在这里插入图片描述
2)当脚本文件无可执行权限时,可以使用以下三种方式运行脚本:
方法一: sh westos.sh—手动在环境中开启指定解释器
在这里插入图片描述
方法二: . westos.sh—直接在当前环境中运行shell中的指令不开启新的shell
在这里插入图片描述
方法三: source westos.sh—直接在当前环境中运行shell中的指令不开启新的shell
在这里插入图片描述
3)当脚本文件有可执行权限时,可以使用以下两种方式运行脚本:
方法一: 绝对路径方式/mnt/westos.sh
方法二: ./westos.sh(本质上和方法一相同,因为.表示的就是当前路径)
以上这两种方式都是开启脚本中指定的shell并使用此shell环境运行脚本中的指令
在这里插入图片描述
4)脚本有无权限时运行方式的区别:新建脚本文件linux.sh输入cat命令(执行脚本时会一直处于运行状态,便于查看实验效果)
在这里插入图片描述
当脚本文件无可执行权限时使用 . linux.sh方式运行脚本,Ctrl+z打入后台,ps f用树状结构表示进程程序间的相互关系,可以看到脚本运行时是在当前shell中直接运行cat命令,fg调回前台后Ctrl+c结束脚本运行
在这里插入图片描述
当脚本文件有可执行权限时使用绝对路径方式运行脚本,Ctrl+z打入后台,ps f用树状结构表示进程程序间的相互关系,可以看到脚本运行时是在当前shell中再开了一个shell环境(#!/bin/bash指定的环境)后运行cat命令
在这里插入图片描述

5、如何对脚本进行调试

实验步骤:
1)创建并编写脚本westos.sh,写入命令如下
在这里插入图片描述
2)由于脚本中写入的cat命令格式错误,运行脚本时会一直处于运行状态,我们可以在运行脚本时使用-x 参数显示脚本运行过程,通过显示的信息来调试脚本,显示的信息中+表示运行的指令,不带+表示指令运行的结果,可以看到脚本在执行cat命令时出错
在这里插入图片描述
命令练习:
1)编写脚本host_messages.sh显示当前主机的名称、ip和登陆当前主机的用户
思路:使用hostname命令可以查看显示当前主机的名称;使用ifconfig ens3查看网卡信息后,通过管道使用awk命令截取inet关键字后没有字符的行的第二列即可得到当前主机的ip;使用$USERwhoami命令可以显示登陆当前主机的用户(\\t表示一个tab键,\\n表示换行,输出中有\\t、\\n时前面必须加-e参数执行动作)
在这里插入图片描述
在这里插入图片描述
2)编写脚本clear_log.sh,执行此脚本后可以清空日志
思路:首先对执行脚本的用户进行是否为超级用户的判断,如果不是超级用户则退出运行并提示以超级用户身份执行此脚本($0表示脚本本身),如果是超级用户则检测日志文件/var/log/messages是否存在,如果不存在则退出运行并提示日志文件不存在,如果存在则使用> /var/log/messages命令清空日志并显示日志已清理
在这里插入图片描述在这里插入图片描述

以上是关于shell脚本基础之详解基本脚本的构建的主要内容,如果未能解决你的问题,请参考以下文章

6.shell脚本基础和grep文本处理工具企业应用

Shell之/bin/bash脚本的基础实战

Shell之/bin/bash脚本的基础实战

牛!一文详解企业级Shell脚本编程!(建议收藏)

第10章:构建基本的脚本_《shell脚本大全 第二版》

shell脚本构建基本脚本