shell脚本编程系列
处理信号
Linux利用信号与系统中的进程进行通信,通过对脚本进行编程,使其在收到特定信号时执行某些命令,从而控制shell脚本的操作。
- Linux信号
shell脚本编程会遇到的最常见的Linux系统信号如下表所示:
在默认情况下,bash shell会忽略收到的任何SIGQUIT(3)信号和SIGTERM(15)信号(因此交互式shell才不会被意外终止)。
bash shell会处理接收到的所有SIGHUP(1)和SIGINT(2)信号。
如果收到SIGHUP信号,比如离开交互式shell时,bash shell就会退出,但是在退出之前,它会将SIGHUP信号传给所有由该shell启动的进程,包括正在运行的脚本。
如果收到SIGINT信号,shell会被中断,Linux内核将不再为shell分配CPU处理时间。当出现这种情况时,shell会将SIGINT信号传给由其启动的所有进程。
shell脚本默认行为时忽略Linux信号,因为不利于脚本运行,要避免这种情况,可以在脚本中加入识别信号的代码,并做相应的处理。
- 产生信号
中断进程 CTRL+C组合键会生成SIGINT信号
暂停进程 Ctrl+Z组合键会生成SIGTSTP信号,停止shell中运行的任何进程。停止进程和终止进程不同,前者让程序驻留在内存中,还能从上次停止的位置继续运行。
- 捕获信号
trap commands signals
trap命令可以指定shell脚本需要侦测并拦截的Linux信号。在commands部分列出想要shell执行的命令,在signals部分列出要捕获的信号(多个信号之间以空格分隔),指定信号时,可以使用信号的值或信号名#!/bin/bash #Testing signal trapping # trap "echo ' Sorry! I have trapped Ctrl-C'" SIGINT # echo This is a test script. # count=1 while [ $count -le 5 ] do echo "Loop #$count" sleep 1 count=$[ $count + 1 ] done # echo "This is the end of test script." exit
trap "" SIGINT
允许脚本完全忽略SIGINT信号,继续执行重要的工作 - 捕获脚本退出
除了在shell脚本中捕获信号,也可以在脚本退出是捕获信号,这也是shell完成任务时执行命令的一种简便方法。要捕获脚本退出,只需要在trap命令后加上EXIT信号即可#!/bin/bash #Testing exit trapping # trap "echo Goodbye..." EXIT # count=1 while [ $count -le 5 ] do echo "Loop #$count" sleep 1 count=$[ $count + 1 ] done # exit
- 修改或移除信号捕获
重新使用带有新内容的trap命令可以修改信号捕获
在trap命令与希望恢复默认行为的信号之间加上两个连字符可以移除设置好的信号捕获
trap -- SIGINT
#!/bin/bash #Removing a set trap # trap "echo ' Sorry...Ctrl-C is trapped.'" SIGINT # count=1 while [ $count -le 3 ] do echo "Loop #$count" sleep 1 count=$[ $count + 1 ] done # trap -- SIGINT echo "The trap is now removed." # count=1 while [ $count -le 3 ] do echo "Second Loop #$count" sleep 1 count=$[ $count + 1 ] done # exit
以后台模式运行脚本
以后台模式执行脚本,只需要在脚本名后面加上&即可,比如 ./backgroundscript.sh &
,&符号会将脚本与当前shell分离开来,并将脚本作为一个独立的后台进程运行
在指定之后,会在控制台输出类似于[1] 2595的字样,其中方括号中的1是shell分配给后台进程的作业号,而后面的2595是Linux系统为进程分配的进程ID(PID)。Linux系统中的每个进程都必须有唯一的PID
当后台进程运行时,它仍然会使用终端显示器来显示STDOUT和STDERR信号
#!/bin/bash
#Test running in the background
#
echo "Starting the script..."
count=1
while [ $count -le 5 ]
do
echo "Loop #$count"
sleep 1
count=$[ $count + 1 ]
done
#
echo "Script is completed."
exit
每次启动新作业时,Linux系统都会为其分配新的作业号和PID。在终端会话中使用后台进程一定要小心,每一个后台进程都和终端会话(pts/0)关联在一起,如果终端会话退出,那么后台进程也会随之退出。
在非控制台下运行脚本
nohup命令能阻断发给特定进程的SIGHUP信号,这样当终端会话退出时,就可以避免进程退出,格式为nohup command
由于nohup命令会将进程与终端的关联解除,所以进程不再同STDOUT和STDERR绑定在一起,默认情况下,nohup命令会将二者产生的消息重定向到一个名为nohup.out的文件中
nohup ./testAscript.sh &
如果使用nohup命令运行了另一个命令,则输出会追加到已有的nohup.out文件中。当运行同一目录中的多个命令时,一定要当心,因为所有的命令输出都会发送到同一个nohup.out文件中
作业控制
作业控制包括启动、停止、杀死和恢复作业
通过jobs命令查看作业
其中-l选项会列出进程的PID以及作业号
-n 只列出上次shell发出通知后状态发生改变的作业
-r 只列出运行中的作业
-s 只列出已停止的作业
删除已停止的作业,只需要使用kill命令向其pid发送SIGKILL(9)信号即可
要以后台模式重启作业,可以使用bg命令
如果存在多个作业,则需要在bg命令后加上作业号,以便于控制
要以前台模式重启作业,可以使用带有作业号的fg命令,比如fg 2
调整谦让度
在多任务操作系统中,内核负责为每个运行的进程分配CPU时间。调度优先级也称为谦让度(nice value),是指内核为进程分配的CPU时间(相对于其他进程)。在Linux系统中,由shell启动的所有进程的调度优先级默认都是相同的
调度优先级是一个整数值,取值范围从-20(最高优先级)到+19(最低优先级)。默认情况下, bash shell以优先级0来启动所有进程
nice命令允许在启动命令时设置调度优先级,通过选项-n指定即可
nice -n 10 ./jobcontrol.sh > jobcontrol.out &
只有root用户或这特权用户才能提高作业的优先级
使用renice命令可以修改系统中已运行命令的优先级
定时运行作业
使用at命令可以调度作业,对于定期运行的脚本不方便,而Linux系统使用cron程序调度需要定期执行的作业。
cron在后台运行,会检查一个特殊的cron时间表,从中获取已安排执行的作业
每天的10:15运行一个命令,cron表达式为:15 10 * * * command
每周一的下午4:15执行的命令 15 16 * * 1 command
每月第一天的12点执行命令 00 12 1 * * command
crontab -l 可以列出已有的时间表
crontab -e会进入一个文件编辑器,添加任务
如果要执行的脚本精确度要求不高,可以用预设置的cron脚本目录即可,预配置的基础目录共有4个:hourly、daily、monthly和weekly
ls /etc/cron.*ly
如果你的脚本需要每天执行一次,那么将脚本复制到daily目录,cron就会每天执行它
使用新shell启动脚本
用户登录shell的时候会执行一些启动文件,比如.bash_profile、.bashrc.
在主目录下的.bashrc文件中加入一条简单的echo语句,那么每次启动一个新的shell时都会执行这个语句.
总结
Linux系统允许使用信号来控制shell脚本。bash shell可以接收信号并将其传给由shell进程生成的所有进程。Linux信号可以轻而易举地杀死失控的进程或暂停耗时的进程。
可以在脚本中用trap命令捕获信号并执行特定命令。这个功能提供了一种简单的方法来控制脚本运行时用户能够将其中断。
在默认情况下,当在终端会话shell中运行脚本时,交互式shell会被挂起,直到脚本运行完毕。你可以在命令后加上一个&符号使脚本或命令以后台模式运行。当在后台模式运行命令或脚本时,交互式shell会被返回,允许你继续输入其他命令。
通过这种方法运行的后台进程仍与终端会话绑定。如果退出终端会话,那么后台进程也会随之退出。nohup命令可以租着这种情况发生。该命令可以拦截任何会导致命令停止运行的信号(比如退出终端会话的信号)。如此一来,即便已经退出了终端会话,脚本也能继续在后台执行。
当将进程置入后台时,仍然可以对其施加控制。jobs命令可以查看shell会话启动的进程。只要知道了后台进程的作业号,就能用kill命令向该进程发送信号,或者用fg命令将该进程带回shell会话的前台。你可以用ctrl+z组合键挂起正在运行的前台进程,也可以用bg命令将其置于后台模式。
nice命令和renice命令可以调整进程的优先级。通过降低进程的优先级,可以使其他高优先级进程获得更多的CPU时间。当运行消耗大量CPU时间的长期进程时,这项特性特别方便。
除了控制处于运行状态的进程,还可以决定何时启动进程。无须直接在命令行界面运行脚本。可以将进程安排在指定时间运行,实现方法不止一种,at命令允许在预设的时间运行脚本,cron程序提供了定时运行脚本的接口,anacron可以确保及时运行脚本(服务器关闭过程中的任务在启动之后会执行)。
最后,Linux系统提供了一些脚本文件,可以让脚本在启动新的bash shell时运行,与此类似,位于用户主目录的启动文件提供了一个位置,以存放新shell启动时需要运行的脚本和命令。