知识体系:
#回顾信号功能
#隐藏在背景中
#在没有控制台的情况下运行
#做得更好
#准确无误的运行
#从头开始
前面运行脚本的方式都是在命令行界面运行,实际上还有运行shell脚本的其他方式,以及中断脚本的运行进程,控制脚本的运行时间都可以实现。
1、处理信号 linux下有各种信号,如停止、启动、终止。通过信号控制shell脚本的运行只需要使得shell脚本接收来自linux体系特定信号时执行命令即可。 1.1、linux信号回顾 系统和应用程序可以生产30多个linux信号,如下罗列出常用的linux系统信号: ****************************************************** 信号 值 描述 1 SIGHUP 挂起进程 2 SIGINT 中断进程 3 SIGQUIT 停止进程 9 SIGKILL 无条件终止进程 15 SIGTERM 如果可能的话终止进程 17 SIGSTOP 无条件停止,但不终止进程 18 SIGTSTP 停止或暂停进程,但不终止 19 SIGCONT 重新启动停止的进程 ******************************************************* 默认情况下,bash shell忽略接收到的任何SIGQUIT和SIGTERM信号,以防止交互的shell意外终止。但是bash shell接收任何SIGHUP和SIGINT信号。 1.2、生成信号 1》中断进程 使用ctrl+c组合键可以生产SIGINT信号,比如用sleep命令测试: [root@wzp ~]# sleep 100 如果我不使用组合键,那么控制台就无法进行输入了,一直运行该sleep程序,所以通过这方法可以终止进程。 2》暂停进程 有些进程想暂停而不是终止它,可以使用ctrl+z组合键生产SIGTSTP信号 [root@wzp ~]# sleep 100 [1]+ Stopped sleep 100 看到没有,如果是暂停进程,会有log信息显示stopped的。 如上可以看到中括号里面有一个1数值,这个就是shell分配的作业编号,第一个启动的进程分配作业编号1,第二个启动的进程分配作业编号2,依此类推,如果shell会话中存在停止的作业,退出shell会发出警告的: [root@wzp ~]# exit exit There are stopped jobs. [root@wzp ~]# exit 退出时说存在着被暂停的作业,不过你再次输入exit可以终止了进程强行退出shell,或者说你可以通过Kill命令发出SIGKILL命令终止它: [root@wzp ~]# sleep 100 [1]+ Stopped sleep 100 [root@wzp ~]# sleep 200 [2]+ Stopped sleep 200 [root@wzp ~]# ps au USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND root 5007 0.0 0.0 4812 496 pts/0 T 16:19 0:00 sleep 100 root 5009 0.0 0.0 4812 496 pts/0 T 16:19 0:00 sleep 200 root 5025 0.0 0.1 5356 944 pts/0 R+ 16:19 0:00 ps au [root@wzp ~]# exit exit There are stopped jobs. [root@wzp ~]# kill -9 5007 [root@wzp ~]# kill -9 5009 [1]- 已杀死 sleep 100 [2]+ 已杀死 sleep 200 这样子通过kill进程号达到终止了进程。 1.3、捕获信号 trap命令可以指定通过shell脚本监控和拦截信号,使得信号不被shell处理,而在本地处理它,其格式为: trap commands signals 下面看个例子,使用trap命令来忽略SIGINT和SIGTERM信号的简单示例: [root@wzp ~]# chmod u+x 6.1test [root@wzp ~]# cat 6.1test #!/bin/bash trap "echo you can not stop me!" SIGINT SIGTERM echo "this is a test program" count=1 while [ $count -le 10 ] do echo "loop #$count" sleep 3 count=$[ $count + 1 ] done echo "the program is over" [root@wzp ~]# ./6.1test this is a test program loop #1 loop #2 loop #3 you can not stop me! loop #4 loop #5 loop #6 you can not stop me! loop #7 loop #8 you can not stop me! loop #9 loop #10 the program is over 当如上程序每隔3秒显示一次信息的时候,我通过ctrl+c暂停程序的时候,该信号都被忽略了,并且在trap命令下回显echo you can not stop me!内容,并且程序一直执行完毕。由此可见trap命令的强大哈! 1.4、捕获脚本退出 除了上面在shell脚本中捕获信号之外,还可以在shell脚本退出那瞬间捕获,只需要在trap命令后添加EXIT信号,看下例子: [root@wzp ~]# cat 6.1test #!/bin/bash trap "echo yeah, the program is over" EXIT count=1 while [ $count -le 5 ] do echo "loop #$count" sleep 3 count=$[ $count + 1 ] done [root@wzp ~]# ./6.1test loop #1 loop #2 loop #3 loop #4 loop #5 yeah, the program is over 当脚本准备退出之际,就会触发trap,并且捕获了EXIT信号显示echo内容 当然,如果你ctrl+c终止进程也是会捕获EXIT信号的,暂停进程则不会。 1.5、移除捕获 我们可以使用破折号来移除捕获,使得shell捕获信号的功能失效,例子: [root@wzp ~]# cat 6.1test #!/bin/bash trap "echo yeah, the program is over" EXIT count=1 while [ $count -le 5 ] do echo "loop #$count" sleep 3 count=$[ $count + 1 ] done trap - EXIT [root@wzp ~]# ./6.1test loop #1 loop #2 loop #3 loop #4 loop #5 我只是在上面的例子中最后添加了移除捕获的一行,结果脚本退出的捕获被移除了,就没有了捕获信息显示了。 2、以后台模式运行脚本 有些shell脚本在执行中需要等待漫长时间,而且终端回话无法执行其他操作,这个时候就可以使得shell放置到后台运行。 2.1、以后台模式运行 这个很简单就得以实现,只要在运行脚本时在命令后面附带一个&符号即可。 [root@wzp ~]# cat 6.2test #!/bin/bash count=1 while [ $count -le 5 ] do echo "loop #$count" sleep 3 count=$[ $count + 1 ] done [root@wzp ~]# ./6.2test & [5] 7348 [root@wzp ~]# loop #1 loop #2 loop #3 loop #4 loop #5 [5] Done ./6.2test 通过这方法就可以到程序放到后台运行,控制台便可以进行其他操作。 所以可以借用这种方法运行多个程序。 3、在不使用控制台的情况下运行脚本 一个程序运行过程默认情况下会随着中断回话的退出而中断。这个时候就可以借用nohup命令来使得阻塞任何发送到进程的SIGHUP信号。 [root@wzp ~]# cat 6.2test #!/bin/bash count=1 while [ $count -le 5 ] do echo "loop #$count" sleep 3 count=$[ $count + 1 ] done [root@wzp ~]# nohup ./6.2test & [2] 7877 [root@wzp ~]# nohup: appending output to “nohup.out” [root@wzp ~]# [2]- Done nohup ./6.2test 因为nohup将进程和终端断开,所以进程没有STDOUT和STDERR输出链接。nohup命令将自动把这两类消息重定向到nohup.out这个自动新创建的文件中去: [root@wzp ~]# cat nohup.out loop #1 loop #2 loop #3 loop #4 loop #5 这个文件的内容跟命令行运行进程输出是完全一样的! 4、作业控制 shell运行的进程可以通过ctrl+z中断,利用kill终止进程,可以使用发送SIGCONT信号重启停止的进程。对于重启、停止。终止、恢复作业的操作就叫做作业控制。 4.1、查看作业 通过jobs命令可以直接查看shell处理的当前作业,看例子: [root@wzp ~]# nohup ./6.2test & [1] 8477 [root@wzp ~]# nohup: appending output to “nohup.out” [root@wzp ~]# nohup ./6.2test & [2] 8479 [root@wzp ~]# nohup: appending output to “nohup.out” [root@wzp ~]# jobs [1]- Running nohup ./6.2test & [2]+ Running nohup ./6.2test & [root@wzp ~]# jobs [1]- Done nohup ./6.2test [2]+ Done nohup ./6.2test 我运行两次同样的脚步程序到后台,然后通过jobs查看,显示running状态,等到脚步运行结束再次jobs查看即可显示已完成状态。 [root@wzp ~]# nohup ./6.2test & [2] 8594 [root@wzp ~]# nohup: appending output to “nohup.out” [root@wzp ~]# jobs [2]+ Running nohup ./6.2test & [root@wzp ~]# kill 8594 [root@wzp ~]# jobs [2]+ 已终止 nohup ./6.2test 当脚本一开始运行到后台会显示了PID号8594,当脚本还没运行结束我kill掉进程,也可以通过jobs查看出进程已经被kill了。 4.2、重新启动停止的作业 我们可以通过ctrl+z暂停进程,当要重新启动停止的作业时可以通过使用带有作业编号的bg命令,看例子: [root@wzp ~]# nohup ./6.2test nohup: appending output to “nohup.out” [1]+ Stopped nohup ./6.2test [root@wzp ~]# jobs [1]+ Stopped nohup ./6.2test [root@wzp ~]# bg 1 [1]+ nohup ./6.2test & [root@wzp ~]# jobs [1]+ Running nohup ./6.2test & [root@wzp ~]# jobs [1]+ Done nohup ./6.2test 先是把程序放到后台运行,然后马上暂停了进程运行,通过jobs即可查看。然后通过bg命令附带作业编号1重启进程。注意:作业编号在我一开始暂停进程的时候就显示了[1]。 上面的bg命令重启的进程后是放到进程去运行的。如果你想把它放到显示屏运行,即可以使用带编号的fg命令,看例子: [root@wzp ~]# ./6.2test loop #1 loop #2 [1]+ Stopped ./6.2test [root@wzp ~]# fg 1 ./6.2test loop #3 loop #4 loop #5 运行一半的程序被暂停后,通过fg 1又可以调到命令行运行下去了。 5、使脚本更好的运行 linux是一个多任务操作系统,内核负责为系统中运行的每个进程分配CPU时间。而CUP中一次就能运行一个进程,因此内核轮流向每个进程分配CPU时间。从shell启动的所有进程在linux系统上的调度优先级都是相同的。调度优先级是内核相对其他进程分配某一个进程的CPU时间量。 调度优先级是一个整数值,从-20(最高优先级)到+20(最低优先级),默认情况下,bash shell启动所有优先级为0的进程 5.1 nice命令 nice命令可以在启动命令时设置它的调度优先级,要让命令在更低的优先级下运行,可以使用-n选项。看个例子: [root@wzp ~]# nice -n 10 ./6.2test & [1] 9888 这样子,该程序就运行在10优先级下了。还有一点注意的是我是通过root超级用户设定脚本运行的优先级别,如果设置在更低的优先级,对于普通用户即可操作,并且一般是属于该脚本程序的用户所有者。但是普通用户无法设置脚本再更高的优先级别下运行。看例子: [testuser@wzp ~]$ ll 总计 8 -rwxr--r-- 1 testuser root 107 02-15 20:00 6.2test [testuser@wzp ~]$ nice -n 10 ./6.2test & [1] 10164 [testuser@wzp ~]$ loop #1 loop #2 loop #3 运行指定在低优先级是没问题的! [testuser@wzp ~]$ nice -n -10 ./6.2test & [1] 10186 [testuser@wzp ~]$ nice: cannot set niceness: 权限不够 [1]+ Exit 1 nice -n -10 ./6.2test 设定在高优先级下运行报权限不够的错误信息! 5.2、renice命令 有使用需要更改系统中已运行的优先级,就可以使用renice命令,例子: [root@wzp ~]# nohup ./6.2test & [1] 10369 [root@wzp ~]# nohup: appending output to “nohup.out” [root@wzp ~]# renice -10 -p 10369 10369: old priority 0, new priority -10 原先我设定脚本在后台运行,并且默认是0的优先级别。通过renice命令指定-10更高的优先级别,并且通过-p加PID号设定,从log信息我们知道旧的调度优先级被修改了。 6、准确无误的运行 shell脚本有时候需要设定在给定的时间运行,特别是半夜,等业务量没有那么忙的时候往往是SA反而忙碌的时候,我们很容易可以想到通过计划任务是实现这一需求,这里头主要有三种方式: *at 命令 *batch 命令 *cron 表格 6.1、使用at命令调度作业 对于at命令的具体使用方面这里就不罗嗦了,可以看下网上的资料或者linux的复习进阶一讲述。其命令格式、日期格式都是需要注意的。 这里讲述下通过运行atd和sendmail应用程序来获取作业输出: 由于linux使用提交作业的用户的电子邮箱地址作为STDOUT和STDERR,所以任何以STDOUT和STDERR为目的地的输出都通过电子邮件发送给用户。 当需要制定运行某个文件的at计划,可以附带-f选项,如: at -f testfile xx:xx 当制定好at命令后可以通过atq命令查看计划列表; 如果要删除已经制定好的计划列表可以通过atrm命令,附带计划number。 6.2、使用batch命令 batch命令跟at命令不太相同,我们知道at命令是在一个指定的时间运行计划好的命令,而batch则是安排脚本在系统使用率低的时候运行。如果linux处于高负荷下运行,那么batch命令就会延迟提交作业的运行。跟at命令类型的地方是命令的格式,可以通过-f参数从文件中读取,默认从STDIN读取。 6.3、调度定期脚本 如果需要脚本在每天、每周、每月的一个特定时间执行脚本,就可以使用cron程序调度需要运行的作业。cron程序在后台运行,它从特殊表格中查找需要调度运行的作业。 对于cron表格的一些命令格式、时间格式,构建cron表格等内容就不说了,这个直接去找网上资料或者看linux复习进结一的计划任务即可。 不过有点挺搞的提及下:系统管理员想在每个月的最后一天执行脚本,那么cron表格应该怎么写么?我们知道每个月的最后一天有可能是28、29、30、31,那么应该怎么判断这一天呢?答案: 这里可以借助添加if-then语句,用date命令检查明天是否1号即可解决问题,具体的cron表格内容如下: * * * * * if [ `date +%d -d tomorrow` = 01 ]; then ; command 通过这种方法就可以判定每个月最后一天啦~\(≧▽≦)/~