【Linux】Shell脚本

基础知识

  1. #号表示注释。
  2. 在文件的第一行指明运行文件的shell类型,一般用"#! /bin/bash"。
  3. 在命令行或者脚本中,引用变量需要在变量名前面加$符号,一般有两种形式,第一$variable_name,第二种${variable_name},花括号有助于识别变量名的范围。另外出现$符号默认解析为引文变量,如果需要使用美元符号,需在美元符号之前转义,如$5表示5美元。
  4. 将命令输出的结果赋值给变量有两种方式,第一种用一对反引号将命令包裹起来;第二种形式为variable_name=$(command_statement),注意是圆括号不是花括号
  5. 重定向技术:
    1. “大于号命令”将命令的输出发送到一个文件中,语法格式为command>outputfile
    2. “双大于号>>"将命令的输出追加到一个文件中
    3. “小于号<"表示输入重定向,即将文件的内容作为某个命令的输入,语法格式为command<filename
  6. 管道连接,将前一个命令的输出重定向到后一个命令,作为后一个命令的输入,语法格式为 command1 | command2。
  7. 在bash shell中进行数学运算可将数学运算表达式用方括号括起来,以此告知bash,方括号内需进行数学运算,$[math_expression];或者使用expr命令,注意运算符与参数值之间需要留空格。
  8. 可以使用特殊变量$?查看上个已执行命令的退出状态码,如果命令成功结束,退出状态码为0,如果遇到错误,退出状态码会是一个正数,常见的退出状态码有:
退出状态码描述
1未知错误
2不合适的shell命令
126命令不可执行
127未找到命令
128无效的退出参数
130ctrl+C退出命令
255正常范围之外的退出状态码
  1. 环境变量IFS(internel field seporator)称作内部字段分隔符,默认值包括空格、制表符、换行符,当字符串包含内部字段分隔符时候,在for循环中,bash会认为分隔符后是新字段的开始,可以通过赋值语句临时改变IFS环境变量的值。比如需要读取PATH环境变量中的每个值时,可将IFS环境变量设置为冒号。
  2. 在脚本中有许多命令时,默认取最后一个命令的退出状态码作为脚本的退出状态码,可以使用exit n指定一个特定值的退出状态码。
  3. 环境变量$$存储当前进程的PID值。
     

顺序命令

以退出状态码为测试条件

  1. if-then命令结构可改变shell脚本的命令执行顺序,当if后面的命令返回的退出状态码为0时,就执行then后面的命令,否则就执行elif|else后面的命令,具体语法格式为

    if command
    then 
    	commands
    else 
    	commands
    fi
    

    或者将then放置到与if同一行:

	if command; then 
		commands
	else 
		commands
	fi

或者使用elif子选项,设置多个测试条件:

	if command1;then
		commands
	elif command2; then
		commands
	elif command3; then
		commands
	else
	    commands
	fi

注意:

  • commands表示可以接多个命令。
  • else语句并非必须。
  • fi表示if语句结束。
  • 可以嵌套if-then结构。
  • elif与起始的if地位是等价的,它们是并列关系,因为最后只有一个fi结束标示,而采用嵌套结构时,会存在多个fi结束标示。
  • 可以有多个elif结构,elif结构就是完整的if-then结构,只是与起始的if公用一个fi结束标识。
  • 当第一个if或者elif后接的command的退出状态码为0时,后续的elif将直接跳过。
     

以test命令自定义测试条件

默认的if-then语句是通过检测命令的退出状态码来决定代码的执行顺序,可以通过test condition的方式模仿类似python中的if-then结构,即condition条件为真时,test以状态码0退出,当条件非真时,test以非0状态码退出

if test condition
then 
	commands
fi

或者使用方括号形式代替test关键字形式:

if [ condition ]
then
	commands
fi

注意:

  • 当采用方括号形式时,condition与方括号前后要保留空格,否则会报错。
  • 如果只写test命令,后面不接condition,默认返回非零状态码。
  • test命令可以进行数值比较、字符串比较、文件比较三种条件测试。
数值条件测试

数值比较在bash shell中只能对整数型进行比较,不支持浮点型比较:

比较描述
n1 -eq n2检查n1与n2是否相等
n1 -gt n2检查n1是否大于n2
n1 -ge n2检查n1是否大于等于n2
n1 -lt n2检查n1是否小于n2
n1 -le n2检查n1是否小于等于n2
n1 -ne n2检查n1与n2是否不相等
字符串条件测试

在shell中也可以进行字符串比较,特别需要注意比较字符串大小时候,需要转义大于号或者小于号,否则系统会将大于号或者小于号解析成重定向符号;另外比较大小时是按照ASCII码的顺序比较的。

字符串比较描述
str1 = str2检查两个字符串是否相同
str1 != str2检查两个字符串是否不相同
-n str检查字符串是否非空
-z str检查字符串是否为空
str1 > str2检查字符串1是否大于字符串2
str1 < str2检查字符串1是否小于字符串2
文件条件测试

文件测试可以检测linux系统上文件与目录的状态,以下的file表示文件或者目录

比较描述
-e file检查file是否存在
-d file检查file是否存在并且是一个目录
-f file检查file是否存在并且是一个文件
-r、-w、-x file检查file是否存在并且是一个可读、写、可执行
-s file检查file是否存在并且非空
-O file检查file是否存在并且并且为当前用户拥有
-G file检查file是否存在并且默认组与当前用户相同
file1 -nt file2检查file1是否比file2新
file1 -ot file2检查file1是否比file2旧

复合条件测试

if-then结构支持布尔表达式

符号描述
&&逻辑与
||逻辑或
逻辑非

 

if命令的高级特性

用于数学表达式的双圆括号
  1. test命令在数值比较中只能支持简单的整数值比较功能,而双圆括号支持更多高级功能,比如乘方,逻辑取反等。语法格式为(( expression )),双括号内两侧也不再强求留空白,大于小于符号也不需要转义,等号之间也支持留空格了。
  2. 双圆括号工具不仅支持数值比较,还支持为变量赋值,注意整个赋值语句都需要放在双圆括号内。
符号描述
**幂运算
&&逻辑与
||逻辑或
!逻辑反
&位布尔与
|位布尔或
~位布尔反
用于字符串高级比较的双方括号

test命令支持三种条件测试,数值比较、字符串比较与文件、目录状态检验,双圆括号针对数值比较(包括赋值)提供了相比test命令的更多高级功能;双方括号也提供了相对test命令的更加高级的功能,其中支持字符串模式匹配最显著,模式支持正则表达式定义
 

case结构

当变量只有若干候选值,且每个候选值会对应不同的候选方案时,采用case结构比if-then结构代码更清晰,具体语法格式为:

case variable in
pattern1 | pattern2) commands ;;
pattern3) commands;;
*) default commands;;
esac
  • 竖线操作符支持在一行中分隔出多个模式,当其中任意一个模式匹配变量值时,执行后面commands中的命令。
  • commands中的命令,每一个命令单独一行,命令之间不需要加分号,但最后结束时需要加两个分号。
  • 星号捕捉所有与已知模式不匹配的值。
  • case结构的结束标识符是esac。
     

循环命令

改变命令执行顺序除了顺序命令之外,另一大类就是循环命令,使用循环命令,可以循环执行一组命令直至达到了某个特定条件才退出循环体。Bash shell中主要有三类循环,for、while与until结构。
 

for循环

创建一个列表,每次循环时使用列表中的一个值来执行一组定义好的命令,直至列表中的值用完。

bash shell风格的for循环
for variable in list
do 
	commands
done

或者将do关键字与for放置同一行,并用分号分开:

for variable in list; do
	commands
done
  • for循环中每次取列表中的一个值作为变量的当前值,当取列表中最后一个值并执行了预指定的命令后,自动退出for循环,但是变量并没有被删除,其值就是列表中的最后一个值。
  • 不同于python,在bash shell中并未显示的使用方括号将列表值括起来,因为方括号有特定的其它用途。系统是依据环境变量IFS来解析列表中的值,默认环境变量IFS中的值包括空格、制表符、换行符。
  • bash shell中存在特殊的环境变量IFS,内部字段分隔符( internal field separator),IFS默认值包含“空格、制表符、换行符”,当遇到这三种字符中的任意一个,就默认需要分割字符串;由于IFS是特殊的环境变量,可以像PATH环境变量那样,通过赋值的方式临时改变其取值。
     
C语言风格的for循环
for ((variable assignment; condition; iteration process))
do
	commands
done
  • 变量赋值部分指定变量的初始值,然后执行commands,当条件为真时,进行iteration process过程,这个过程改变变量的取值,通常是自身加减1,然后进行循环直至条件为假。
  • C语言形式的for循环支持定义多变量,多个变量之间以逗号隔开,for循环在处理迭代过程时会单独的处理每一个变量,但循环判定条件只能指定一个。
     

while循环

while循环检查某个命令的退出状态码或者借助test命令检查自定义条件是否为真,判断是否执行一组命令。当命令退出状态码为0或者检测条件为真时,执行循环体,否则退出循环。while命令的关键是检测条件的布尔值或者退出状态码需要发生改变,否则将进入死循环

while test_commands 
do 
	commands
done
  • while支持包含多个测试命令或者test测试命令,是否执行循环只取决于最后一个测试命令的状态返回码。
     

until循环

until与while完全相反,当状态码为非0时,执行循环体定义的那组命令,否则不执行。

until test_commands
do
	commands
done

 

循环控制命令

break命令:与python中的break语句略有不同,语法为break -n, 默认值n=1,n表示退出循环的层级,默认退出的范围是break所在的循环,若需要退出break的外层循环,需要使用break -2,即退出包含break的毗邻外层循环。

continue命令:终止本次循环,开启下轮循环,也支持-n参数。若需要对循环体的输出结果重定向或者进行管道连接,在done标识符后接重定向符号或者管道连接符号
 

与shell脚本进行交互

通过命令行参数与shell脚本交互

通过命令行参数向脚本提供数据是最常见的与脚本交互的方式。

  1. bash shell会自动将用户提供的命令行参数赋值给“位置参数”特殊变量,其中位置参数“$0代表文件名”,“$1代表第一个命令行参数”,依次类推,可以像其他变量一样,在脚本中引用位置参数变量。当传递多个命令行参数时,命令行参数以空格分开。
  2. 位置参数变量$0默认获取脚本的名称,当运行脚本时包含路径信息, 位置参数变量$0也会包含路径信息。当需要根据文件名来执行不同功能时,可以借助basename命令、dirname命令。basename与dirname与python的os模块中的basename与dirname相似,dirname命令剥离得到路径信息,如果文件名不含“/",则返回”."。basename命令剥离得到文件名信息。
  3. 特殊变量$#统计命令行参数的个数,可以在脚本内的任何地方引用$#,当不提供命令行参数时,其值为0。
  4. 特殊变量$*或者$@都可以通过单个变量存储所有命令行参数,区别在于,$*会将命令行中的所有参数当做一个整体保存进$*变量,而$@会将所有变量当做独立部分保存在一个字符串中,可以用循环的方式取出各个独立部分。
  5. shift命令移动命令行参数,默认情况下,会将所有命令行参数往左移动一个位置,注意$1向左移动一个位置后相当于被删除,$0就是文件名不受shift影响。可以为shift命令指定参数,表明所有参数每次向左移动的单位数,语法格式 shift n。
     
通过命令行选项与shell脚本交互
  1. shell脚本后面是可以带命令行参数与命令行选项的,当shell脚本中同时包含命令行选项命令行参数的时候,可以使用多种方式告诉脚本,哪些是命令行参数、哪些是命令行选项、哪些是命令行选项需要的额外参数。
  2. getopt命令或者getops命令,解析命令行选项与参数的合法格式
    常用的命令行选项
     
通过read命令与shell脚本交互
  1. read 命令可以通过提问并将用户的输入存入特定变量的方式与shell脚本交互, 语法格式为read -p‘问题提示符’ variable_name,答案会自动存储到自定的变量中
  2. -t选项指定使用计时器,即指定了read等待用户输入的等待秒数;若超时,read会返回非零退出状态码
  3. -s选项会隐藏用户的输出,使其不显示在显示屏
  4. read也可以用来读取文件中的数据,每次调用read,会读取文件中的一行,当读取完文件后,read会退出并返回非零状态码
     

处理shell脚本的输出

linux将所有对象当做文件来处理,并用文件描述符来标识每个文件对象。对于标准输入、标准输出、标准错误分别用文件描述符0、1、2来标识,其中标准输入默认对应键盘,标准输出与标准错误默认对应显示屏,默认标准输出与标准错误是分开处理的

  • 重定向错误信息:当不指定文件描述符仅使用重定向符重定向命令输出时,默认只是重定向标准输出,标准错误仍然会输出到显示屏上。如果需要重定向错误,可以采用将重定向符号与文件描述符结合的方法,如 2>filename,注意文件描述符需要与重定向符号毗邻,中间不能留空格,以此将错误信息单独输出到日志文件。
  • 重定向标准输出与错误信息:在将错误信息保存到日志文件的同时,linux也支持标准输出的重定向,可以将标准输出重定向到日志文件,也可以是另外一个独立的文件,一般而言是将标准输出与标准错误重定向到两个文件,即 命令输出 1>filename1 2>filename2。也通过特殊符号“&”可以将标准输出与标准错误一起重定向到指定文件,如“命令输出 &>filename”,在filename中标准错误的优先级会高于标准输出,会展示在文件的开始出。
  • 重定向到文件描述符:之前都是将正常输出或者错误信息重定向到某个文件,如果需要将信息重定向到文件描述符,则需要结合使用文件描述符、重定向符与&符号,如2>&1,这表示将标准错误信息重定向到标准输出中`。linux最多支持9个文件描述符,其中0-2是保留的文件描述符,3-8是可供用户自定义使用的文件描述符。
  • 在脚本中重定向输出与输入:之前都是在脚本外重定向输出,也可以在脚本内重定向部分输出,包含两种方式,一种是临时重定向,即在需要重定向输出的地方结合使用文件表示符、重定向符号就可以,比如output_info 1>testerror 就将output_info 的信息输入到testerror文件中,另一种就是永久重定向,exec 1>filename可以将脚本中所有指向STDOUT的信息重定向到filename中。也支持在脚本中重定向输入,比如exec 0< filename
  • 阻止信息输出:linux系统有一特殊文件null,输出到该文件中的数据会被自动销毁,因此可利用重定向方法,将输出重定向到/dev/null文件,实现销毁输出的目的。
     

控制脚本运行

信号处理

Linux使用信号与进程进行通信,当bash shell接受到某类型的信号时,shell会将该信号发送给shell内的进程,进程根据默认设置或者脚本中的设置对接受到的信号进行处理,常见的信号名见下面的信号表,也可以在脚本中捕获信号执行自定义的命令,通过默认方式或者自定义方式实现对脚本运行的控制。

比如当shell接受到SIGINT信号时, shell将SIGINT信号发送给脚本程序或者正在运行的命令,当脚本程序或者命令接收到SIGINT信号时,默认就会停止运行。常见的信号如下表,可以通过kill -l查看所有信号值:

信号值信号名描述
1SIGHUP挂起进程
2SIGINT终止进程
3SIGQUIT停止进程
9SIGKILL无条件终止进程
15SIGTERM尽可能终止进程
17SIGSTOP无条件停止进程,但不是终止进程
18SIGTSTP停止进程但不终止
19SIGCONT继续运行停止的进程

默认情况下,当bash shell收到SIGHUP信号后,会将SIGHUP信号发送给bash shell内的所有进程,然后退出shell。当bash shell接受到SIGINT信号后,会将SIGINT信号发送给当前运行的进程,运行的脚本程序或者命令接收到SIGINT命令后,默认会停止。
 

生成信号

bash shell允许使用快捷键生成两种基本信号, ctrl+c 生成SIGINT信号,ctrl+z生成SIGTSTP信号
 

捕获信号

默认情况下,当进程收到信号后会执行停止、暂停、继续等操作,但若需要改变进程收到指定信号后的默认行为,就需要采用捕获信号。捕获信号是指当进程收到指定信号时,会执行自定义的一组命令,具体是通过trap命令实现,语法格式为trap commands signalscommands对应待执行的一组命令,signals对应待捕获的一组信号,信号用空格分开,若要恢复信号对应的默认行为,可以trap与信号值之间加上双破折线或者单扩折现,即trap -- singals 或者 trap - signals
 

后台运行模式

后台运行是指将进程放置在后台运行,进程不占用当前终端会话,即让出命令行提示符,供用户进行其他操作。需要在后台模式中运行脚本或者命令,只需要在命令或者脚本后面加上”&“ 符号,命令或者脚本就会自动进入后台运行模式,注意默认情况下,1)在后台运行时,命令或者脚本的正常信息或者错误信息会输出到显示屏,最好将输出信息进行重定向,否者输出信息将会与让出来的命令提示符及新输入的命令、新输入命令的输出混杂在一起。2)每个后台进程会与一个终端关联起来,当关联的终端退出后,关联的后台进程也会随之退出,如果需要在终端退出时,后台运行的命令或者脚本继续运行,需借助nohup命令。
 

非控制台模型

默认情况下,后台模式运行的进程会与一个终端相关联,当终端退出时,即使后台进程正在运行,也会被强行终止。如果需要在终端退出时,进程仍然能够继续运行,可使用nohup命令。

nohup命令会阻断发送给进程的SIGHUB信号,实现在退出终端控制台时不退出进程的效果,即解除进程与终端控制台的关联。通过nohup与&命令符,可以使命令或者脚本在”非终端控制台上以后台模式“持续运行,nohup script|commands &

由于nohup解除了进程与终端的联系,因此进程就不在与STDIN、STDOUT 、STDERR关联,nohup会将命令或者脚本的输出自动重定向到运行脚本或者命令目录下nohup.out文件(该文件是自动生成的),也可以自定义重定向,特别是在一个目录下以非控制台模式运行多个脚本时,最好为每个脚本进行重定向,否则所有输入会混杂在一个nohup.out文件中,如nohup srcipt1 & >nohup1.out; nohup script2 & >nohup2.out
 

参考资料

《Linux命令行与shell脚本编程大全.第3版》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值