Shell编程—控制脚本
1处理信号
1.1信号表
编号 |
信号名称 |
缺省操作 |
解释 |
1 |
SIGHUP |
Terminate |
挂起控制终端或进程 |
2 |
SIGINT |
Terminate |
来自键盘的中断 |
3 |
SIGQUIT |
Dump |
从键盘退出 |
4 |
SIGILL |
Dump |
非法指令 |
5 |
SIGTRAP |
Dump |
跟踪的断点 |
6 |
SIGABRT |
Dump |
异常结束 |
6 |
SIGIOT |
Dump |
等价于SIGABRT |
7 |
SIGBUS |
Dump |
总线错误 |
8 |
SIGFPE |
Dump |
浮点异常 |
9 |
SIGKILL |
Terminate |
强迫进程终止 |
10 |
SIGUSR1 |
Terminate |
对进程可用 |
11 |
SIGSEGV |
Dump |
无效的内存引用 |
12 |
SIGUSR2 |
Terminate |
对进程可用 |
13 |
SIGPIPE |
Terminate |
向无读者的管道写 |
14 |
SIGALRM |
Terminate |
实时定时器时钟 |
15 |
SIGTERM |
Terminate |
进程终止 |
16 |
SIGSTKFLT |
Terminate |
协处理器栈错误 |
17 |
SIGCHLD |
Ignore |
子进程停止、结束或在被跟踪时获得信号 |
18 |
SIGCONT |
Continue |
如果已停止则恢复执行 |
19 |
SIGSTOP |
Stop |
停止进程执行 |
20 |
SIGTSTP |
Stop |
从tty发出停止进程 |
21 |
SIGTTIN |
Stop |
后台进程请求输入 |
22 |
SIGTTOU |
Stop |
后台进程请求输出 |
23 |
SIGURG |
Ignore |
套接字上的紧急条件 |
24 |
SIGXCPU |
Dump |
超过CPU时限 |
25 |
SIGXFSZ |
Dump |
超过文件大小的限制 |
26 |
SIGVTALRM |
Terminate |
虚拟定时器时钟 |
27 |
SIGPROF |
Terminate |
概况定时器时钟 |
28 |
SIGWINCH |
Ignore |
窗口调整大小 |
29 |
SIGIO |
Terminate |
I/O现在可能发生 |
29 |
SIGPOLL |
Terminate |
等价于SIGIO |
30 |
SIGPWR |
Terminate |
电源供给失败 |
31 |
SIGSYS |
Dump |
坏的系统调用 |
31 |
SIGUNUSED |
Dump |
等价于SIGSYS |
1.2捕获信号
rap命令允许你来指定shell 脚本要监看并从shell中拦截的Linux信号。trap命令的格式是:
trap commands signals
其中commands你是要执行的操作,sianals是信号(可以是信号名,也可以是信号编号)
这里有个简单例子,展示了如何使用trap命令来忽略SIGINT信号,并控制脚本的行为。
$ cat test1.sh #!/bin/bash # Testing signal trapping # trap "echo ' Sorry! I have trapped Ctrl-C'" 2 # 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 the test script"
本例中用到的trap命令会在每次检测到SIGINT(2)信号时显示一行简单的文本消息。捕获这些信号会阻止用户用bash shell组合键Ctrl+C来停止程序。
$ ./test1.sh This is a test script Loop #1 Loop #2 ^C Sorry! I have trapped Ctrl-C Loop #3 ^C Sorry! I have trapped Ctrl-C Loop #4 Loop #5 This is the end of the test script $
每次使用Ctrl+C组合键,脚本都会执行trap命令中指定的echo语句,而不是处理该信号并允许shell停止该脚本。
1.3 捕获脚本退出
除了在shell脚本中捕获信号,你也可以在shell脚本退出时进行捕获。要捕获shell脚本的退出,只要在trap命令后加上EXIT信号就行:
$ cat test2.sh #!/bin/bash # Trapping the script exit trap "echo Goodbye..." EXIT count=1 while [ $count -le 5 ] do echo "Loop #$count" sleep 1 count=$[ $count + 1 ] done $ ./test2.sh Loop #1 Loop #2 Loop #3 Goodbye...
当脚本运行到正常的退出位置时或者是提退出,捕获就被触发了,shell会执行在trap命令行指定的命令。
1.4修改或移除捕获
要想在脚本中的不同位置进行不同的捕获处理,只需重新使用带有新选项的trap命令:
$ cat test3.sh #!/bin/bash # Modifying a set trap trap "echo ' Sorry... Ctrl-C is trapped.'" 2 count=1 while [ $count -le 5 ] do echo "Loop #$count" sleep 1 count=$[ $count + 1 ] done trap "echo ' I modified the trap!'" 2 count=1 while [ $count -le 5 ] do echo "Second Loop #$count" sleep 1 count=$[ $count + 1 ] done
修改了信号捕获之后,脚本处理信号的方式就会发生变化。但如果一个信号是在捕获被修改前接收到的,那么脚本仍然会根据最初的trap命令进行处理。
$ ./test3.sh Loop #1 Loop #2 Loop #3 ^C Sorry... Ctrl-C is trapped. Loop #4 Loop #5 Second Loop #1 Second Loop #2 ^C I modified the trap! Second Loop #3 Second Loop #4 Second Loop #5
也可以删除已设置好的捕获。只需要在trap命令与希望恢复默认行为的信号列表之间加上破折号(-或者--)就行了:
$ cat test3b.sh #!/bin/bash # Removing a set trap trap "echo ' Sorry... Ctrl-C is trapped.'" SIGINT count=1 while [ $count -le 5 ] do echo "Loop #$count" sleep 1 count=$[ $count + 1 ] done # Remove the trap trap -- SIGINT echo "I just removed the trap" count=1 while [ $count -le 5 ] do echo "Second Loop #$count" sleep 1 count=$[ $count + 1 ] done $ ./test3b.sh Loop #1 Loop #2 Loop #3 Loop #4 Loop #5 I just removed the trap Second Loop #1 Second Loop #2 Second Loop #3 ^C
2以后台模式运行脚本
2.1后台运行脚本
以后台模式运行shell脚本非常简单。只要在命令后加个&符就行了。$ cat test4.sh
#!/bin/bash # Test running in the background count=1 while [ $count -le 10 ] do sleep 1 count=$[ $count + 1 ] done $ ./test4.sh & [1] 3231
显示的第一行是:
[1] 3231
方括号中的数字是shell分配给后台进程的作业号。下一个数是Linux系统分配给进程的进程ID(PID)。Linux系统上运行的每个进程都必须有一个唯一的PID。
一旦系统显示了这些内容,新的命令行界面提示符就出现了。你可以回到shell,而你所执行的命令正在以后台模式安全的运行。这时,你可以在提示符输入新的命令
当后台进程结束时,它会在终端上显示出一条消息:
[1] Done ./test4.sh
这表明了作业的作业号以及作业状态(Done),还有用于启动作业的命令。
注意,当后台进程运行时,它仍然会使用终端显示器来显示STDOUT和STDERR消息。所以最好将消息重定向,不然会导致屏幕信息混乱。
2.2运行多个后台作业
可以在命令行提示符下同时启动多个后台作业。
$ ./test6.sh & [1] 3568 $ This is Test Script #1 $ ./test7.sh & [2] 3570 $ This is Test Script #2 $ ./test8.sh & [3] 3573 $ And...another Test script $ ./test9.sh & [4] 3576 $ Then...there was one more test script
每次启动新作业时,Linux系统都会为其分配一个新的作业号和PID。通过ps命令,可以看到所有脚本处于运行状态。
$ ps PID TTY TIME CMD 2431 pts/0 00:00:00 bash 3568 pts/0 00:00:00 test6.sh 3570 pts/0 00:00:00 test7.sh 3573 pts/0 00:00:00 test8.sh 3574 pts/0 00:00:00 sleep 3575 pts/0 00:00:00 sleep 3576 pts/0 00:00:00 test9.sh 3577 pts/0 00:00:00 sleep 3578 pts/0 00:00:00 sleep 3579 pts/0 00:00:00 ps
注意,在ps命令的输出中,每一个后台进程都和终端会话(pts/0)终端联系在一起。如果终端会话退出,那么后台进程也会随之退出。
3 在非控制台下运行脚本
nohup命令运行了另外一个命令来阻断所有发送给该进程的SIGHUP信号。这会在退出终端会话时阻止进程退出。
nohup命令的格式如下:
$ nohup ./test1.sh &
[1] 3856
$ nohup: ignoring input and appending output to 'nohup.out'
和普通后台进程一样,shell会给命令分配一个作业号,Linux系统会为其分配一个PID号。区别在于,当你使用nohup命令时,如果关闭该会话,脚本会忽略终端会话发过来的SIGHUP信号。 6由于nohup命令会解除终端与进程的关联,进程也就不再同STDOUT和STDERR联系在一起。为了保存该命令产生的输出,nohup命令会自动将STDOUT和STDERR的消息重定向到一个名为nohup.out的文件中。
在进程完成运行后,你可以查看nohup.out文件中的输出结果:
$ cat nohup.out This is a test script Loop 1 Loop 2 Loop 3 Loop 4 Loop 5 Loop 6 Loop 7 Loop 8 Loop 9 Loop 10 This is the end of the test script