十一、脚本控制与计划任务(未完)
信号
Linux利用信号与运行在系统中的进程进行通信。
可以编写脚本使其收到特定信号时执行某些命令,从而控制shell脚本的操作。
linux常见信号
信号 | 值 | 描述 |
1 | SIGHUP | 挂起进程 |
2 | SIGINT | 终止进程 |
3 | SIGQUIT | 停止进程 |
9 | SIGKILL | 强制终止进程 |
15 | SIGTERM | 正常终止进程 |
17 | SIGSTOP | 强制停止进程 |
18 | SIGTSTP | 停止或暂停进程 |
19 | SIGCONT | 继续运行停止的进程 |
默认情况下bash shell会忽略收到的3,15信号,这样使交互式shell不会意外终止。
bash shell会接收1,2信号,如离开一个交互式shell之前,shell会把1信号即挂起信号传给所有由该shell启动的进程。
通过2信号,中断shell,Linux内核会停止为shell分配CPU处理时间,同样shell会把该信号传给由他启动的进程。
生成信号
linux使用键盘组合键生成停止或暂停两种基本信号
中断进程
ctrl+c生成2信号,终止进程
[root@tzPC ~]# sleep 100 ^C
暂停进程
ctrl+z生成18信号,停止shell中运行的进程,停止进程会让程序继续保存在内存中并能从上次暂停的位置继续运行。
[1]是shell分配的作业号,shell把运行中的每个进程称为作业并为其分配唯一的作业号
[root@tzPC ~]# sleep 100 ^Z [1]+ 已停止 sleep 100
jobs查看后台停止的进程
[root@tzPC ~]# jobs [1]+ 已停止 sleep 100
将后台停止的进程恢复到前台运行
[root@tzPC ~]# fg 1
如果shell会话中有一个已停止的作业再退出shell时bash会提醒你
[root@tzPC ~]# sleep 100 ^Z [1]+ 已停止 sleep 100 [root@tzPC ~]# exit exit 有停止的任务。
使用ps -l查看已停止的作业
[root@tzPC ~]# ps -l F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD 4 S 0 89099 89094 0 80 0 - 28912 do_wai pts/0 00:00:00 bash 0 T 0 91144 89099 0 80 0 - 26976 do_sig pts/0 00:00:00 sleep 0 R 0 91231 89099 0 80 0 - 37235 - pts/0 00:00:00 ps
知道了已停止作业的PID,可以使用kill命令发送SIGKILL信号终止他
[root@tzPC ~]# kill -9 91144
捕获信号
trap命令
捕获信号并执行其他命令
语法格式
trap commands signals
signals可以是数值或linux信号名
例:使用trap命令忽略SIGINT信号,捕获ctrl+c组合键
效果
[root@tzPC 16Unit]# bash test1.sh This is a test script Loop #1 Loop #2 Loop #3 ^CSorry! I have trapped Ctrl-C Loop #4 Loop #5 ^CSorry! I have trapped Ctrl-C Loop #6 Loop #7 Loop #8 Loop #9 Loop #10
脚本
[root@tzPC 16Unit]# cat test1.sh #!/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 10 ] do echo "Loop #$count" sleep 1 count=$[ $count + 1 ] done
捕获脚本退出
捕获shell脚本退出只要在trap命令后加上EXIT信号即可。
效果
[root@tzPC 16Unit]# cat test2.sh #!/bin/bash #Trapping the script exit trap "echo 脚本执行完毕." EXIT count=1 while [ $count -le 5 ] do echo "Loop #$count" sleep 1 count=$[ $count + 1 ] done
如果提前退出同样能捕获到EXIT信号
[root@tzPC 16Unit]# bash test2.sh Loop #1 Loop #2 Loop #3 ^C脚本执行完毕.
修改或移除捕获
例:修改捕获命令
[root@tzPC 16Unit]# bash test3.sh Loop #1 Loop #2 ^CSorry...Ctrl+C is trapped. Loop #3 Loop #4 Loop #5 Second #1 ^CI modified the trap! Second #2 Second #3 Second #4 Second #5
脚本
[root@tzPC 16Unit]# cat test3.sh #!/bin/bash #Modifying 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 trap "echo 'I modified the trap!'" SIGINT #这里修改了捕获到SIGINT的命令 count=1 while [ $count -le 5 ] do echo "Second #$count" sleep 1 count=$[ $count + 1 ] done
删除捕获,在trap跟信号名称之间加--即可
trap -- SIGINT
或者
trap - SIGINT
脚本
[root@tzPC 16Unit]# cat test3.sh #!/bin/bash #Modifying 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 trap - SIGINT #或者trap -- SIGINT同样的效果 echo "I just removed the trap" count=1 while [ $count -le 3 ] do echo "Second #$count" sleep 1 count=$[ $count + 1 ] done
后台模式运行脚本
后台运行的脚本echo默认输出到显示器,最好重定向到文件里,不然会让人头昏脑胀,哈哈
运行多个后台作业
[root@tzPC 16Unit]# bash test4.sh & [2] 89870 [root@tzPC 16Unit]# bash test5.sh & [3] 90239 [root@tzPC 16Unit]# bash test6.sh & [4] 90421
ps查看
每一个后台进程都和终端会话pts/0终端联系在一起,如果终端会话退出那么后台进程也会退出
[root@tzPC 16Unit]# ps PID TTY TIME CMD 77372 pts/0 00:00:00 bash 89870 pts/0 00:00:00 bash 89872 pts/0 00:00:00 sleep 90239 pts/0 00:00:00 bash 90240 pts/0 00:00:00 sleep 90421 pts/0 00:00:00 bash 90422 pts/0 00:00:00 sleep 92024 pts/0 00:00:00 ps 115344 pts/0 00:00:00 bash
在非控制台下运行脚本
即使退出终端也让脚本一直运行的方法
之前有些过一篇笔记screen命令就可以做到
传送门:https://www.cnblogs.com/tz90/p/13289340.html
书里讲的nohup命令
nohup命令会阻断所有发送给该进程的SIGHUP信号,也就是关闭session的信号。
运行脚本,此时session窗口假设为1
[root@tzPC 16Unit]# cat test4.sh #!/bin/bash count=1 while true do echo "hello,$count" sleep 2 count=$[ $count + 1 ] done [root@tzPC 16Unit]# nohup bash test4.sh nohup: 忽略输入并把输出追加到"nohup.out"
打开另外一个session窗口2查看进程
[root@tzPC ~]# ps aux | fgrep test4.sh root 22674 0.0 0.1 113132 1416 pts/0 S+ 16:36 0:00 bash test4.sh root 23685 0.0 0.0 112676 896 pts/2 R+ 16:37 0:00 grep -F --color=auto test4.sh
关闭session1窗口,再次查看进程发现进程还在
nohup命令会解除session跟进程的关联也就是说关闭终端nohup命令执行的命令依旧在后台执行,这样造成nohup命令不再通STDOUT和STDERR描述符联系在一起,也就输出不到屏幕上。
这时nohup命令会自动将STDOU和STDERR消息重定向到当前目录下名为nohup.out文件中。
参考:https://blog.csdn.net/hl449006540/article/details/80216061
作业控制
作业停止后可以使用kill命令终止该进程,要重启停止的进程需要发送SIGCONT信号。
启动、停止、终止以及恢复作业的功能统称为作业控制。
查看作业
jobs可以查看正在处理的作业
脚本案例
[root@tzPC 16Unit]# cat test10.sh #!/bin/bash #Test job control echo "Script Process ID: $$" count=1 while [ $count -le 10 ] do echo "Loop #$count" sleep 10 count=$[ $count + 1 ] done echo "End of script..."
运行脚本使用Ctrl+Z停止脚本
[root@tzPC 16Unit]# bash test10.sh Script Process ID: 63760 Loop #1 ^Z [1]+ 已停止 bash test10.sh
同样运行脚本使用&在后台进程中启动
这里将输出重定向到文件,避免头昏脑胀哈哈(我喜欢书里的这句话)!
[root@tzPC 16Unit]# bash test10.sh > test10.out & [2] 64666
使用jobs查看
[root@tzPC 16Unit]# jobs [1]+ Stopped bash test10.sh [2]- Running bash test10.sh > test10out &
jobs -l 选项查看作业的PID
[root@tzPC 16Unit]# jobs -l [1]+ 68838 Stopped bash test10.sh [2]- 68855 Running bash test10.sh > test10out &
上图所示,带+的作业是默认作业,在使用未指定作业号的作业控制命令时,该作业会被当初作业控制命令的操作对象。
带-的作业会成为下一个默认作业。
任何时候都只有一个带加号跟带减号的作业。
jobs常用命令参数
参数 描述 -l 列出进程的PID跟作业好 -n 只列出上次shell发出的通知后改变了状态的作业 -p 只列出作业的PID -r 只列出运行中的作业 -a 只列出已停止的作业
重启停止的作业
bg命令可以将已停止的作业作为后台进程重启。
[root@tzPC 16Unit]# bash test10.sh >test10out ^Z [1]+ Stopped bash test10.sh > test10out [root@tzPC 16Unit]# jobs [1]+ Stopped bash test10.sh > test10out [root@tzPC 16Unit]# bg [1]+ bash test10.sh > test10out & [root@tzPC 16Unit]# jobs [1]+ Running bash test10.sh > test10out &
因为该作业时默认作业所以bg后面不用加作业号
fg命令可以将已停止的作业作为前台进程重启。
[root@tzPC 16Unit]# jobs [1]+ Stopped bash test10.sh > test10out [root@tzPC 16Unit]# fg 1 bash test10.sh > test10out
谦让度
- 调度优先级时内核分配给进程的CPU时间,优先级越高进程占用CPU的时间也就越多
- 调度优先级是个整数值
- 取值范围从-20(最高优先级)到+19(最低优先级)
- 由shell启动的所有进程调度优先级默认都相同,都为0
- 需要root权限
nice命令
-n参数修改优先级
[root@tzPC 16Unit]# nice -n 10 bash test4.sh >test4out & [1] 72440
查看优先级NI
[root@tzPC 16Unit]# ps -p 72440 -o pid,ppid,ni,cmd PID PPID NI CMD 72440 68701 10 bash test4.sh
renice命令
用于更改系统上已经运行命令的优先级,需要用到已运行命令进程的PID
- 只能对属主进程执行renice
- 普通用户只能降低进程优先级
- root用户可以调高跟降低进程优先级
[root@tzPC 16Unit]# bash test5.sh & [1] 73522 [root@tzPC 16Unit]# ps -p 73522 -o pid,ppid,ni,cmd PID PPID NI CMD 73522 68701 0 bash test5.sh [root@tzPC 16Unit]# renice -n 10 -p 73522 73522 (process ID) old priority -10, new priority 10 [root@tzPC 16Unit]# ps -p 73522 -o pid,ppid,ni,cmd PID PPID NI CMD 73522 68701 10 bash test5.sh
定时运行作业
- at(一次性执行)
- cron(定期执行)
- anacron(定期执行)
用at命令计划执行脚本
- 必须运行atd服务
- atd守护进程会每隔60秒检查/var/spool/at目录下文件,并同时检查文件设置的运行时间,如果跟当前时间匹配,则运行。
- 如果错过指定的时间,at命令会在第二天指定时间运行文件。
启动atd服务并加入开机启动项
[root@tzPC 16Unit]# systemctl start atd #必须启动atd服务
[root@tzPC 16Unit]# systemctl enable atd #加入开机启动项
at命令格式
at -f filename time
time格式
10:15
10:15 PM
now、noon、midnight、teatime(4 PM)
MMDDYY、MM/DD/YY、DD.MM.YY
Jul 4、Dec 25
时间增量(当前时间+25min、明天10:15PM、10:15+7天)
优先级
支持26中不同的优先级,以小写字母a-z和大写字母A-Z标识,默认为a优先级
a的优先级最高,z的优先级最低
使用-q参数指定优先级
at命令的输出
脚本文件的输出会发送到该用户的邮件
案例
-f指定运行脚本文件,now表示立刻执行该脚本
[root@tzPC 16Unit]# cat test13.sh #!/bin/bash #Test using at command echo "This script ran at $(date +%B%d,%T)" >test13out sleep 5 echo "This is the script's end..." >>test13out [root@tzPC 16Unit]# at -f test13.sh now job 4 at Fri Aug 21 16:40:00 2020
注意:最好将脚本文件的输出重定向到文件,因为如果没有安装sendmail,脚本的输出结果就获取不了
列出等待中的作业
[root@tzPC 16Unit]# at -M -f test10.sh teatime job 5 at Sat Aug 22 16:00:00 2020 [root@tzPC 16Unit]# at -M -f test10.sh tomorrow job 6 at Sat Aug 22 16:47:00 2020 [root@tzPC 16Unit]# at -M -f test10.sh 13:30 job 7 at Sat Aug 22 13:30:00 2020 [root@tzPC 16Unit]# at -M -f test10.sh now job 8 at Fri Aug 21 16:47:00 2020 [root@tzPC 16Unit]# atq 5 Sat Aug 22 16:00:00 2020 a root 6 Sat Aug 22 16:47:00 2020 a root 7 Sat Aug 22 13:30:00 2020 a root 8 Fri Aug 21 16:47:00 2020 = root
删除等待中的作业
artm 7
cron命令定期执行脚本
使用cron命令,前提条件是系统是7X24小时运行,如果计划运行时间系统正好处于关机状态,这个计划任务就不会运行,即使开机也不会运行错过的计划任务。
启动crond服务并加入开机启动项
[root@tzPC 16Unit]# systemctl start crond
[root@tzPC 16Unit]# systemctl enable crond
cron时间定义格式
min hour dayofmonth month dayofweek command
dayofmonth表示月份中的日期值(1-31日)
每天10:15运行命令
15 10 * * * command
每周一下午4:15运行命令
15 16 * * 1 command
在每月最后一天执行命令(这里书中命令有误),应该如下写法
00 12 1 * * if [ `date +%d -d tomorrow` = 01 ];then command ;fi
命令列表必须指定脚本绝对路径,同时也可给脚本参数或者重定向到文件
15 10 * * * /home/tz/test4.sh > test4out
-e参数创建cron计划任务
[root@tzPC 16Unit]# crontab -e
15 10 * * * /root/script/16Unit/test10.sh >test10out
查看cron时间表
[root@tzPC 16Unit]# crontab -l 15 10 * * * /root/script/16Unit/test10.sh >test10out
浏览cron目录
有4个基本目录
hourly、daily、monthly、weekly
分别对应每小时、每天、每月、每周运行一次
将脚本放入对应目录即可
[root@tzPC etc]# ls /etc/cron.*ly /etc/cron.daily: logrotate man-db.cron /etc/cron.hourly: 0anacron /etc/cron.monthly: /etc/cron.weekly:
anacron命令
此命令执行的计划任务如果错过了执行时间,会尽快运行错过的计划任务。
常用于清理日志文件
该命令只会处理位于cron目录的脚本
用时间戳来确定脚本是否在规定时间内运行,每个cron目录都有个时间戳文件,位于/var/spool/anacron
如
[root@tzPC etc]# cat /var/spool/anacron/cron.monthly 20200817
查看anacron时间表
[root@tzPC etc]# cat /etc/anacrontab # /etc/anacrontab: configuration file for anacron # See anacron(8) and anacrontab(5) for details. SHELL=/bin/sh PATH=/sbin:/bin:/usr/sbin:/usr/bin MAILTO=root # the maximal random delay added to the base delay of the jobs RANDOM_DELAY=45 # the jobs will be started during the following hours only START_HOURS_RANGE=3-22 #period in days delay in minutes job-identifier command 1 5 cron.daily nice run-parts /etc/cron.daily 7 25 cron.weekly nice run-parts /etc/cron.weekly @monthly 45 cron.monthly nice run-parts /etc/cron.monthly
anacron时间定义格式
period delay identifier command
#待补充
学习来自:《Linux命令行与Shell脚本大全 第3版》第16章