第十五章:控制脚本

处理信号

重温Linux信号

信号 名称 描述
1 HUP 挂起
2 INT 中断
3 QUIT 结束运行
9 KILL 无条件终止
11 SEGV 段错误
15 TERM 尽可能终止
17 STOP 无条件停止运行,但不终止
18 TSTP 停止或暂停,但继续在后台运行
19 CONT 在STOP或TSTP之后恢复执行

默认情况下,bash shell会忽略收到的任何SIGQUIT(3)和SIGTERM(5)信号。

如果bash shell收到了SIGHUP信号,它会退出。但是在退出之前,它会将SIGHUP信号传给shell启动的所有进程(比如shell脚本)。通过SIGINT可以中断shell。Linux内核会停止将CPU的处理时间分配给shell。此时,shell会将SIGINT信号传给shell启动的所有进程。

注意:shell会将这些信号传给shell脚本程序来处理,而shell脚本的默认行为是忽略这些信号。

产生信号

终止进程

CTRL+C会生成SIGINT信号

暂停进程

CTRL+Z会生成SIGSTP信号

$ gedit a
^Z
[1]+ Stopped gedit a

这里看到有一个作业产生,状态为stopped

关于作业讲解可参看(#1)

可以用ps查看已停止的作业,然后用kill将其终止。

捕捉信号

trap commands signal

指定shell脚本观察的信号并拦截

#!/bin/bash
trap "echo you can\'t stop me!" SIGINT count=1
while [ $count -le 10 ]
do
echo "Loop #$count"
count=$[ $count + 1]
sleep 5
done

执行脚本时候按下CTRL+C,那么此时脚本不会终止,而是打出log

$ signal_test
Loop #1
^Cyou can't stop me!
Loop #2
^Cyou can't stop me!
……

虽然不能终止它,但是发现,本来应该sleep 5秒的,CTRL+C之后,这次sleep就马上结束了

捕捉脚本的退出

同上面一样,只需把trap捕捉的信号改为EXIT即可

trap "echo bye!" EXIT

当脚本退出的时候则会输出:

$ signal_test
Loop #1
^Cbye!

移出捕捉

trap - signals

在连词线后面加信号即可。

以后台模式运行脚本

后台运行脚本

只需在命令后加&即可

此时会输出作业ID和进程ID

$ gedit signal_test &
[1] 9651

运行多个后台作业

$ gedit redirect_test &
[2] 9689
[1] Done gedit signal_test
$ signal_test &
[3] 9699

通过ps au可以看到程序执行情况

1000      9689  2.5  0.4 395676 29944 pts/0    Sl   20:12   0:00 gedit redirect_test

1000      9699  0.0  0.0  13624  1468 pts/0    S    20:12   0:00 /bin/bash ./signal_test

退出终端

通过ps的输出,可以看到每个后台进程都绑定到了该终端会话的终端上了(pts/0).如果进程会话退出了,后台进程也会退出。

退出终端时,如果有关联到终端的还在运行的后台进程,有的终端模拟器会提醒你,但不是全部。

在非控制台下运行脚本

nohup命令可以帮助你使得脚本一直在后台运行,直到其完成,即使退出了终端会话。

nohup command args...

$ nohup gedit signal_test &
[1] 9878
$ nohup: ignoring input and appending output to `nohup.out'

如果关闭该会话,脚本会忽略任何终端发过来的SIGHUP信号

由于nohup命令会从终端解除进程的关联,进程会丢掉到STDOUT和STDERR的链接。

为了保存输出,nohup会自动将STDOUT和STDERR重定向到名为nohup.out的文件中

注意:如果使用nohup执行了多个命令,那么这些输出都会重定向到nohup.out中!

作业控制

启动、停止、无条件终止以及恢复作业的这些功能统称为作业控制。

查看作业

jobs可以列出分配给shell的作业

-l:列出进程的PID以及作业号

-n:只列出上次shell发出的通知后改变了状态的作业

-p:只列出作业的PID

-r:只列出运行中的作业

-s:只列出已停止的作业

$ gedit test.xml &
[1] 5463
$ jobs
[1]+ Running gedit test.xml &

带加号的作业会被当成默认的作业。使用作业命令时,如果未指定作业号,那么该作业会被当做操作对象。

带减号的作业会在当前默认作业处理完毕的时候成为下一个默认作业。

任何时候都不会有超过1个的带加号的作业和带减号的作业

$ signal_test
Loop #1
^Z
[1]+ Stopped signal_test
$ signal_test
Loop #1
^Z
[2]+ Stopped signal_test
$ signal_test
Loop #1
^Z
[3]+ Stopped signal_test
$ jobs -l
[1] 5596 Stopped signal_test
[2]- 5598 Stopped signal_test
[3]+ 5602 Stopped signal_test
$ kill -9 5602
$ jobs -l
[1]- 5596 Stopped signal_test
[2]+ 5598 Stopped signal_test

kill了3号任务之后,之前带减号的2号任务就变成了当前的任务

重启停止的作业

bg jobid:在后台重启任务

fg jobid:在前台重启任务

(fg、bg更多讲解可参看(#1))

调整谦让度

调度优先级是个整数,从-20(最高优先级)到20(最低优先级)。默认情况下,bash shell以优先级=0启动所有进程。

nice命令

nice -n level command

普通用户无法设置更高优先级,会得到如下错误

$ nice -n -1 gedit
nice: cannot set niceness: Permission denied

renice命令

renice level -p pid

使用renice时需要注意:

1.只能对属于你的进程执行

2.只能降低优先级

3.root用户可以随意调整任何进程任何优先级

定时运行作业

atd守护进程会检查系统上的一个特殊目录(通常为/var/spool/at。我机器上面没有这个目录,在我机器上应该是/var/spool/cron/atjobs)

默认情况下,atd守护进程会每60s检查一下这个目录。有作业时,atd守护进程会检查作业设置运行的时间。

at命令的格式

at [-f filetime

默认情况下,at会将STDIN的输入放到队列中。-f可以指定执行的脚本文件。

下面是at可以识别的时间格式:

标准小时和分钟,比如10:11

~AM/~PM指示符,比如10:32~PM

特定可命名的时间:比如now、noon、midnight、teatime(4~PM)

如果指定的时间已经过去了,那么at会在第二天这个时候执行

标准日期格式:比如MMDDYY、MM/DD/YY、DD.MM.YY

文本日期,比如Jul 4,有无年份均可

也可指定时间增量

当前时间+25min

明天11:32~PM

11:50+4天

使用at命令,作业会被提交到作业队列(job queue)中。有26种不同优先级的队列,用小写a-z表示。

作业队列字母排序越高,优先级就越低。可以使用-q执行优先级。

$ at -f for_test noon
warning: commands will be executed using /bin/sh
job 8 at Thu Aug 29 12:00:00 2013
$ sudo ls /var/spool/cron/atjobs/
a00008015e6130
$ at -q c -f for_test noon
warning: commands will be executed using /bin/sh
job 9 at Thu Aug 29 12:00:00 2013
$ sudo ls /var/spool/cron/atjobs/
a00008015e6130 c00009015e6130

当任务执行完毕的时候,/var/spool/cron/atjobs/下的文件会被删除

注意:

使用at的时候,我们会发现有这样一行log输出

warning: commands will be executed using /bin/sh

也就是说,系统会用/bin/sh来执行脚本,而不是使用bash!

这样就会遇到一些问题,足以让人崩溃。

大多数:inux发行版中,赋给/bin/sh的默认shell是bash shell,但是Ubuntu将dash shell作为其默认shell。

为了使用bash执行脚本,我们需要做个wrapper

bash /home/su1216/android/source/linux_learned/for_test

保存为文件,我这里命名它为bash_wrapper

然后用at执行此脚本即可

$ at -f bash_wrapper now
warning: commands will be executed using /bin/sh
job 27 at Thu Aug 29 16:08:00 2013

这个时候,世界将恢复正常。

获取作业的输出

作业运行时,屏幕不会有输出。系统会将提交作业的用户Email作为STDOUT和STDERR。

在机器上要安装mailutils

之后执行mail,可能会出现下面的问题:

$ mail
Cannot open mailbox /var/mail/su1216: Permission denied
No mail for su1216

可以按着下列步骤解决:

sudo touch /var/mail/$USER
sudo chown $USER:mail /var/mail/$USER
sudo chmod o-r /var/mail/$USER
sudo chmod g+rw /var/mail/$USER

上面的命令之前都已经讲过,意义分别为

创建/var/mail/$USER文件

改变属主

去除其他用户的读权限

给属组增加读写权限

列出等待的作业

atq可以列出等待的作业

$ atq
30 Fri Aug 30 16:28:00 2013 a su1216

删除作业

atrm job

注意:只能删除自己提交的作业

计划定期执行脚本

Linux系统使用cron来定期执行作业。cron会在后台运行并检查称作cron时间表(cron table)来获得计划执行的作业。

cron时间表

cron时间表格式如下:

min hour dayofmonth month dayofweek command

cron时间表允许使用特定的值、范围、通配符指定时间。比如:

15 10 * * * command

在dayofmonth、month和dayofweek字段中使用通配符,表示字段值的可以取到的全集。上面表示在每天的10:15都要执行command

可以使用三字符文本值(mon、tue……)或者数值(0=周日,6=周六)指定dayofweek

注意:如何指定每月的最后一天

可以使用date来查看明天的日期是不是01

00 12 *  * * if [ `date +%d -d tomorrow` = 01 ] ; then ; command

在每天上午12点的时候检查明天是不是01

cron时间表必须指定命令的全路径。可以添加任何参数和重定向符号

cron程序是假定Linux是7*24小时运行的!

如果脚本还没有执行,系统就关闭了,过了执行时间再开启机器,那么cron是不会执行过期的脚本的。

构建cron时间表

每个用户都有自己的cron时间表

crontab -l:列出当前用户的cron时间表,默认cron时间表是不存在的。可以使用-e参数来添加条目。

cron目录

当不要求有精确的时间执行脚本时,用预配置的cron脚本目录会更方便。

有4个基本目录

$ ls /etc/cron.*ly
/etc/cron.daily:
0anacron apport apt bsdmainutils dpkg exim4-base logrotate man-db mlocate popularity-contest quota samba standard /etc/cron.hourly: /etc/cron.monthly:
0anacron /etc/cron.weekly:
0anacron apt-xapian-index cvs man-db

每天执行一次,则只需把脚本拷贝到 /etc/cron.daily下面即可。

anacron程序

anacron和cron不同,他会处理因为关机而过期的任务。

anacron只会处理cron目录的程序。它用时间戳来决定作业是否在适当的计划间隔内运行了。每个cron目录都有时间戳文件,位于/var/spool/anacron

anacron程序有自己的用来检查作业目录的表,通常位于/etc/anacrontab

anacron时间表于cron时间表格式不同,具体如下:

period delay identifier command

period单位为天,delay系统启动多少分钟后,anacron开始执行错过的脚本。command包括run-parts程序和一个cron脚本目录。run-parts程序负责运行目录中传给它的任何脚本。

注意:anacron不会运行位于/etc/cron.hourly的脚本

identifier是一个特殊的非空白字符串,它用于唯一识别日志消息和错误Email中的作业。

下面是我机器上面的anacrontab

$ cat /etc/anacrontab
# /etc/anacrontab: configuration file for anacron # See anacron(8) and anacrontab(5) for details. SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin # These replace cron's entries
1 5 cron.daily nice run-parts --report /etc/cron.daily
7 10 cron.weekly nice run-parts --report /etc/cron.weekly
@monthly 15 cron.monthly nice run-parts --report /etc/cron.monthly

启动时运行

开机时运行脚本

开机过程

开始运行Linux系统时,Linux内核会加载到内存中并运行。它做的第一件事是开始UNIX System V过程或Upstart init过程(具体取决于发行版和版本)。然后这个过程将负责启动Linux系统上所有其他进程。

System V init过程

System V init过程会读取/etc/inittab文件。inittab文件会列出系统的运行级(run level)

基于Red Hat发行版的Linux运行级别

运行级别 描述
0 关机
1 单用户模式
2 多用户模式,通常不支持网络
3 全功能多用户模式,支持网络
4 可定义用户
5 多用户模式,支持网络和图形化X Window会话
6 重启

基于Debian发行版的Linux运行级别(包括Ubuntu、Linux Mint等)

运行级别 描述
0 关机
1 单用户模式
2-5 多用户模式,支持网络和图形化X Window会话
6 重启

Ubuntu没有/etc/inittab文件,默认情况下会以运行级别2启动系统,想要修改,需要自行创建/etc/inittab文件

有些Linux发行版将开机脚本放在/etc/rc#.d,其中#代表运行级别。有些放在/etc/init.d,有些放在/etc/init.d/rc.d下面

Upstart init过程

Upstart不关注系统运行级别,而关注时间。

在Upstart中,系统开机称为开机事件(startup event)。

Upstart使用位于/etc/event.d或/etc/init目录下的文件来启动进程,具体取决于发行版和版本。

为了向后兼容,许多Upstart实现仍会调用较早的位于/etc/init.d以及/etc/rc#.d目录中的System V init脚本

定义自己的开机脚本

Linux本地开机文件位置

发行版 文件位置
debian /etc/init.d/rc.local
Fedora /etc/rc.d/rc.local
Mandriva /etc/rc.local
OpenSure /etc/init.d/boot.local
Ubuntu /etc/rc.local

可以修改本地开机文件,使用脚本时,必须指定脚本的全路径。

警告:不同Linux发行版在开机过程的不同事件执行该本地开机脚本。有时该脚本会在网络支持等启动前运行。

在新shell中启动

bash shell会用主目录下的两个文件.bash_profile和.bashrc来自动启动脚本并设置环境变量

当新shell是新的登录生成的话,bash shell会运行.bash_profile文件。可以把任何登录时要运行的脚本放到该文件中。

当新shell启动时,包括有新的登录的情况,bash shell 会运行.bashrc文件。

如果想为系统中所有用户运行一个脚本。大多数Linux发行版提供了/etc/bashrc文件

更多登录shell和非登录shell内容参见(#2

1.《Unix & Linux 大学教程》 - 第二十六章 进程和作业控制

2.《Unix & Linux 大学教程》 - 第十四章:使用shell:初始化文件

转贴请保留以下链接

本人blog地址

http://su1216.iteye.com/

http://blog.csdn.net/su1216/

《Linux命令行与shell脚本编程大全》 第十五章 学习笔记的更多相关文章

  1. 《Linux命令行与shell脚本编程大全》 第二十七章 学习笔记

    第二十七章:shell脚本编程进阶 监测系统统计数据 系统快照报告 1.运行时间 uptime命令会提供以下基本信息: 当前时间 系统运行的天数,小时数,分钟数 当前登录到系统的用户数 1分钟,5分钟 ...

  2. 《Linux命令行与shell脚本编程大全》 第二十三章 学习笔记

    第二十三章:使用数据库 MySQL数据库 MySQL客户端界面 mysql命令行参数 参数 描述 -A 禁用自动重新生成哈希表 -b 禁用 出错后的beep声 -B 不使用历史文件 -C 压缩客户端和 ...

  3. 《Linux命令行与shell脚本编程大全》 第三章 学习笔记

    第三章:基本的bash shell命令 bash程序使用命令行参数来修改所启动shell的类型 参数 描述 -c string 从string中读取命令并处理他们 -r 启动限制性shell,限制用户 ...

  4. 《Linux命令行与shell脚本编程大全》 第五章理解shell

    5.1 1. cat /etc/passwd 可以查看每个用户自己的默认的shell程序. 2.默认的交互shell会在用户登录某个虚拟控制台终端时启动. 不过还有另外一个默认的shell是/bin/ ...

  5. 《Linux命令行与shell脚本编程大全》 第六章环境变量

    很多程序和脚本都通过环境变量来获取系统信息.存储临时数据和配置信息. 6.1 什么是环境变量: bash shell用一个叫环境变量(environment variable)的特性来存储有关shel ...

  6. 《Linux命令行与shell脚本编程大全》第十三章 更多的结构化命令

    本章讨论bash shell的循环命令for.while和until 13.1 for命令 重复执行一系列命令在编程中很常见. bash shell提供了for命令,允许你创建一个遍历一系列值的循环. ...

  7. 《Linux命令行与shell脚本编程大全》第二十一章 sed进阶

    本章介绍一些sed编辑器提供的高级特性. 21.1 多行命令 按照之前的知识,所有的sed编辑器命令都是针对单行数据执行操作的. 在sed编辑器读取数据流时,它会基于换行符的位置将数据分成行,一次处理 ...

  8. 《Linux命令行与shell脚本编程大全》第十一章 构建基本脚本

    11.1使用多个命令 $date;who   //  命令列表,加入分号就可以,这样会依次执行.参见5.2.1节 注意区分$(date;who),这个是进程列表,会生成一个子shell来执行 Shel ...

  9. 《Linux命令行与Shell脚本编程大全第2版》读书笔记

    公司说不准用云笔记了,吓得我赶紧把笔记贴到博客上先..... 近3年前的了,只有一半的章节,后面的没空记录了.... 第1章 可以cat /proc/meminfo文件来观察Linux系统上虚拟内存的 ...

  10. 《Linux命令行与shell脚本编程大全》第十七章 创建函数

    可以将shell脚本代码放进函数中封装起来,这样就能在脚本中的任何地方多次使用它了. 17.1 基本的脚本函数 函数:是一个脚本代码块,可以为其命名并在代码中任何位置重用. 17.1.1 创建函数 有 ...

随机推荐

  1. 关于jQuery中toggle()函数的使用

    今天遇到一个有趣的例子,将它记录下来. 一个一级菜单,里边有一个二级菜单,二级菜单是通过锚点来链接页面元素的.想要实现的效果是当点击锚点时,页面链接到相应锚点,同时二级菜单隐藏,再点击一级菜单时,继续 ...

  2. Latex笔记-基本布局

    转自https://linhan.blog.ustc.edu.cn/?p=135&cpage=1 目录改用中文标题并且居中 \renewcommand{\contentsname}{\cent ...

  3. Vmware 克隆CentOS 网络IP配置

    在VMware里克隆出来的CentOS Linux.. ifconfig...没有看到eth0..然后重启网卡又报下面错误. 故障现象: service network restart Shuttin ...

  4. Sql优化(三) 关于oracle的并发

    Oracle的并发技术可以将一个大任务分解为多个小任务由多个进程共同完成.合理地使用并发可以充分利用系统资源,提高效率.一. 并发的种类Parallel queryParallel DML(PDML) ...

  5. Oracle EBS-SQL (SYS-4):sys_职责查询.sql

    select t.RESPONSIBILITY_NAME from apps.FND_RESPONSIBILITY_VL t where t.RESPONSIBILITY_NAME like '%MR ...

  6. 如何在C++中获得完整的类型名称(RTTI的typeid在不同平台下有不同的输出值表达,自建类改进了RTTI丢失的信息)

    Wrote by mutouyun. (http://darkc.at/cxx-get-the-name-of-the-given-type/)   地球人都知道C++里有一个typeid操作符可以用 ...

  7. Get Intellisense for .axml files in Visual Studio

    原文Get Intellisense for .axml files in Visual Studio So in order to get some intellisense support for ...

  8. Qt通过Http上传文件(路过)

    web端: <?php $c = $GLOBALS['HTTP_RAW_POST_DATA']; $n = $_GET["filename"]; $fp = fopen($n ...

  9. 「深入理解计算系统」从Hello World开始

    从 hello world 开始 Table of Contents 1 程序源文件 2 程序源文件是什么 3 程序被编译 4 程序运行 4.1 读取命令 4.2 读取指令内容 4.3 执行过程 5 ...

  10. 07.20 html5的适配flexible

    <script src="http://g.tbcdn.cn/mtb/lib-flexible/0.3.4/??flexible_css.js,flexible.js"> ...