第一部分:Linux命令行
    《Linux命令行与shell脚本编程大全》 第一章:初识Linux shell
    《Linux命令行与shell脚本编程大全》 第二章:走进shell
    《Linux命令行与shell脚本编程大全》 第三章:基本的bash shell命令
    《Linux命令行与shell脚本编程大全》 第四章:更多的bash shell命令
    《Linux命令行与shell脚本编程大全》 第五章:使用Linux环境变量
    《Linux命令行与shell脚本编程大全》 第六章:理解Linux文件权限
    《Linux命令行与shell脚本编程大全》 第七章:管理文件系统
    《Linux命令行与shell脚本编程大全》 第八章:安装软件程序
    《Linux命令行与shell脚本编程大全》 第九章:使用编辑器

    第二部分:shell脚本编程基础
    《Linux命令行与shell脚本编程大全》 第十章:构建基本脚本
    《Linux命令行与shell脚本编程大全》 第十一章:使用结构化命令
    《Linux命令行与shell脚本编程大全》 第十二章:更多的结构化命令
    《Linux命令行与shell脚本编程大全》 第十三章:处理用户输入
    《Linux命令行与shell脚本编程大全》 第十四章:呈现数据
    《Linux命令行与shell脚本编程大全》 第十五章:控制脚本

    第三部分:高级shell编程
    《Linux命令行与shell脚本编程大全》 第十六章:创建函数
    《Linux命令行与shell脚本编程大全》 第十七章:图形化桌面上的脚本编程
    《Linux命令行与shell脚本编程大全》 第十八章:初识sed和gawk
    《Linux命令行与shell脚本编程大全》 第十九章:正则表达式
    《Linux命令行与shell脚本编程大全》 第二十章:sed进阶
    《Linux命令行与shell脚本编程大全》 第二十一章:gawk进阶
    《Linux命令行与shell脚本编程大全》 第二十二章:使用其他shell

    第四部分:高级shell脚本编程主题
    《Linux命令行与shell脚本编程大全》 第二十三章:使用数据库
    《Linux命令行与shell脚本编程大全》 第二十四章:使用Web
    《Linux命令行与shell脚本编程大全》 第二十五章:使用E-mail
    《Linux命令行与shell脚本编程大全》 第二十六章:编写脚本实用工具
    《Linux命令行与shell脚本编程大全》 第二十七章:shell脚本编程进阶


    第十五章:控制脚本


    处理信号

    重温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 file ] time

    默认情况下,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/