shell脚本学习七(作业控制)

shell脚本学习七(作业控制)

Scroll Down

命令执行过裎中按 Ctrl+Z 快捷键,命令在后台处于暂停状态

**进程: **

进程是一个程序对某个数据集的执行过程,是分配资源的基本单位。

 作业:

作业是用户需要计算机完成的某项任务,是要求计算机所做工作的集合

作业的调度属于高级调度,进程的调度属于低级调度

作业就是从外存放到内存的一个过程,它可以包含一个或多进程。

        进程和作业的概念也有区别。一个正在执行的进程称为一个作业,而且作业可以包含一个或多个进程,尤其是当使用了管道和重定向命令。例如 “nroff -man ps.1|grep kill|more” 这个作业就同时启动了三个进程。
        作业控制指的是控制正在运行的进程的行为。比如,用户可以挂起一个进程,等一会儿再继续执行该进程。shell 将记录所有启动的进程情况,在每个进程过程中,用户可以任意地挂起进程或重新启动进程。作业控制是许多 shell(包括 bash 和 tcsh)的一个特性,使用户能在多个独立作业间进行切换。
       一般而言,进程与作业控制相关联时,才被称为作业。

       在大多数情况下,用户在同一时间只运行一个作业,即它们最后向 shell 键入的命令。但是使用作业控制,用户可以同时运行多个作业,并在需要时在这些作业间进行切换。这会有什么用途呢?例如,当用户编辑一个文本文件,并需要中止编辑做其他事情时,利用作业控制,用户可以让编辑器暂时挂起,返回 shell 提示符开始做其他的事情。其他事情做完以后,用户可以重新启动挂起的编辑器,返回到刚才中止的地方,就象用户从来没有离开编辑器一样。这只是一个例子,作业控制还有许多其他实际的用途。

 jobs: 该命令用于查看当前终端后台运行的任务。

ps: 该命令用于查看瞬间进程的动态

控制脚本:

 1、处理信号

      1.1  重温常见 LInux 信号

          信  号               值                     描  述

             1              SIGHUP             挂起进程

             2              SIGINT               终止进程 

             3             SIGQUIT             停止进程

             9              SIGKILL         无条件终止进程

            15          SIGTERM         尽可能终止进程

            17           SIGSTOP      无条件停止进程,但不是终止进程

            18           SIGTSTP      停止或暂停进程,但不终止进程

            19           SIGCONT      继续运行停止的进程 

          默认情况下,bash shell 会忽略收到的任何 SIGQUIT (3) 和 SIGTERM (15) 信号(正因为这样, 交互式 shell 才不会被意外终止)。但是 bash shell 会处理收到的 SIGHUP (1) 和 SIGINT (2) 信号。

        如果 bash shell 收到了 SIGHUP 信号,比如当你要离开一个交互式 shell,它就会退出。但在退 出之前,它会将 SIGHUP 信号传给所有由该 shell 所启动的进程(包括正在运行的 shell 脚本)。

        通过 SIGINT 信号,可以中断 shell。Linux 内核会停止为 shell 分配 CPU 处理时间。这种情况发 生时,shell 会将 SIGINT 信号传给所有由它所启动的进程,以此告知出现的状况。

       你可能也注意到了,shell 会将这些信号传给 shell 脚本程序来处理。而 shell 脚本的默认行为 是忽略这些信号。它们可能会不利于脚本的运行。要避免这种情况,你可以脚本中加入识别信 号的代码,并执行命令来处理信号。

       1.2  生成信号 

            ① 中断进程

           Ctrl+C 组合键会生成 SIGINT 信号,并将其发送给当前在 shell 中运行的所有进程。可以运行一 条需要很长时间才能完成的命令,然后按下 Ctrl+C 组合键来测试它

           

            Ctrl+C 组合键会发送 SIGINT 信号,停止 shell 中当前运行的进程。sleep 命令会使得 shell 暂停 指定的秒数,命令提示符直到计时器超时才会返回。在超时前按下 Ctrl+C 组合键,就可以提前终 止 sleep 命令。

            ②  暂停进程 

            Ctrl+Z 组合键会生成一个 SIGTSTP 信号,停止 shell 中运行的任何进程。停止(stopping)进程跟终止(terminating)进程不同:停止进程会让程序继续保留在内存中,并能从上次停止的位置继续运行

              

           方括号中的数字是 shell 分配的作业号(job number)。shell 将 shell 中运行的每个进程称为作业, 并为每个作业分配唯一的作业号。它会给第一个作业分配作业号 1,第二个作业号 2,以此类推。

           如果你的 shell 会话中有一个已停止的作业,在退出 shell 时,bash 会提醒你

              

          用 ps 命令来查看已停止的作业      

          在 S 列中(进程状态),ps 命令将已停止作业的状态为显示为 T。这说明命令要么被跟踪,要么被停止了。

          如果在有已停止作业存在的情况下,你仍旧想退出 shell,只要再输入一遍 exit 命令就行了。 shell 会退出,终止已停止作业。或者,既然你已经知道了已停止作业的 PID,就可以用 kill 命令 来发送一个 SIGKILL 信号来终止它

           

           在终止作业时,最开始你不会得到任何回应。但下次如果你做了能够产生 shell 提示符的操作 (比如按回车键),你就会看到一条消息,显示作业已经被终止了。每当 shell 产生一个提示符时, 它就会显示 shell 中状态发生改变的作业的状态。在你终止一个作业后,下次强制 shell 生成一个提示符时,shell 会显示一条消息,说明作业在运行时被终止了。

          

         1.3  捕获信号 

           trap 命令允许你来指定 shell 脚本要监看并从 shell 中拦截的 Linux 信号。如果脚本收到了 trap 命令中列出的信号,该信号不再由 shell 处理,而是交由本地处理。 

           trap 命令的格式是: 

                  trap commands signals   

          这里有个简单例子,展示了如何使用 trap 命令来忽略 SIGINT 信号,并控制脚本的行为

               

          本例中用到的 trap 命令会在每次检测到 SIGINT 信号时显示一行简单的文本消息。捕获这些信号会阻止用户用 bash shell 组合键 Ctrl+C 来停止程序

            正常运行:

                

           用户主动使用 Ctrl - C 尝试中断进程:

               

           此例中,每次使用 Ctrl+C 组合键,脚本都会执行 trap 命令中指定的 echo 语句,而不是处理该信号并 允许 shell 停止该脚本

           1.4 捕获脚本退出

                除了在 shell 脚本中捕获信号,也可以在 shell 脚本退出时进行捕获。这是在 shell 完成任务时 执行命令的一种简便方法。 要捕获 shell 脚本的退出,只要在 trap 命令后加上 EXIT 信号就行

               

              

              当脚本运行到正常的退出位置时,捕获就被触发了,shell 会执行在 trap 命令行指定的命令。 如果提前退出脚本,同样能够捕获到 EXIT

          1.5 修改或移除捕获

               在脚本中的不同位置进行不同的捕获处理,只需重新使用带有新选项的 trap 命令即可

               

              修改了信号捕获之后,脚本处理信号的方式就会发生变化。但如果一个信号是在捕获被修改 前接收到的,那么脚本仍然会根据最初的 trap 命令进行处理

              

             移除捕获:

               只需要在 trap 命令与希望恢复默认行为的信号列表之间加上两个破折号就可以删除已设置好的捕获。

            

           

           ------------------------------------------------------------------------------------------------------------------------------------

            也可以在 trap 命令后使用单破折号来恢复信号的默认行为。单破折号和双破折号都可以正常发挥作用

          --------------------------------------------------------------------------------------------------------------------------------------

          移除信号捕获后,脚本按照默认行为来处理 SIGINT 信号,也就是终止脚本运行。但如果信 号是在捕获被移除前接收到的,那么脚本会按照原先 trap 命令中的设置进行处理       

2、以后台模式运行脚本

          2.1  后台运行脚本

               后台运行脚本 只用在运行命令上加上一个 & 符即可

               (  注意: 放入后台执行的命令不能与前台有交互,否则这个命令是不能在后台执行的 )

              

             当 & 符放到命令后时,它会将命令和 bash shell 分离开来,将命令作为系统中的一个独立的后 台进程运行。

             [1] 2884 

             方括号中的数字是 shell 分配给后台进程的作业号。下一个数是 Linux 系统分配给进程的进程 ID(PID)。Linux 系统上运行的每个进程都必须有一个唯一的 PID。

             一旦系统显示了这些内容,新的命令行界面提示符就出现了。你可以回到 shell,而你所执行 的命令正在以后台模式安全的运行。这时,你可以在提示符输入新的命令

             当后台进程结束时,它会在终端上显示出一条消息: 
                  [1] +  Done                    sleep  2 

            这表明了作业的作业号以及作业状态(Done),还有用于启动作业的命令。

            注意,当后台进程运行时,它仍然会使用终端显示器来显示 STDOUT 和 STDERR 消息

           (  即交互式界面仍然会显示  )

            

           

           你会注意到上面两个例子中,命令、脚本的输出与 shell 提示符混杂在了一起。 

          处理方法: 最好是将 STDOUT 和 STDERR 进行重定向,避免这种杂乱的输出

          

         在终端会话中使用后台进程时一定要小心。注意,在 ps 命令的输出中,每一个后台进程都和 终端会话(pts/0)终端联系在一起。如果终端会话退出,那么后台进程也会随之退出

       2.2  在非控制台下运行脚本

           用 nohup 命令来实现,即使退出了终端会话脚本也会一直以后台模式运行到结束

           原理就是:nohup 命令运行了另外一个命令来阻断所有发送给该进程的 SIGHUP 信号。这将会在退出终端会话时阻止进程退出

           当你使用 nohup 命令时,如果关闭该会话,脚本会忽略终端会话发过来的 SIGHUP 信号。
           由于 nohup 命令会解除终端与进程的关联,进程也就不再同 STDOUT 和 STDERR 联系在一起。 为了保存该命令产生的输出,nohup 命令会自动将 STDOUT 和 STDERR 的消息重定向到一个名为 nohup.out 的文件中         

          

          -------------------------------------------------------------------------------------------------------------------------------------------------------

       说明  如果使用 nohup 运行了另一个命令,该命令的输出会被追加到已有的 nohup.out 文件中。当 运行位于同一个目录中的多个命令时一定要当心,因为所有的输出都会被发送到同一个 nohup.out 文件中,结果会让人摸不清头脑

          ------------------------------------------------------------------------------------------------------------------------------------------------------- 

         

         

      2.3 作业控制

           在本篇博客的前面部分,你已经得知如何使用组合键停止 shell 中正在运行的作业。在作业停止后, Linux 系统会让你选择是终止还是重启。你可以用 kill 命令终止该进程。要重启停止的进程需要 向其发送一个 SIGCONT 信号。 

         启动、停止、终止以及恢复作业的这些功能统称为作业控制。通过作业控制,就能完全控制 shell 环境中所有进程的运行方式了。本节将介绍用于查看和控制在 shell 中运行的作业的命令。 

         2.3.1 查看作业

            作业控制中的关键命令是 jobs 命令。jobs 命令允许查看 shell 当前正在处理的作业。 

        

           脚本使用 $$ 变量来显示 Linux 系统分配给该脚本的 PID,然后进入循环,每次迭代都休眠 10 秒。
           可以从命令行中启动脚本,然后使用 Ctrl+Z 组合键来停止脚本

           

           第二次使用同样的脚本,利用 & 将另外一个作业作为后台进程启动。出于简化的目的,脚本的输出被重定向到文件中,避免出现在屏幕上。( 此处如果不使用 > 重定向,即使将其放入后台,仍会与前台有互动产生输出 )

           jobs 命令可以查看分配给 shell 的作业。jobs 命令会显示这两个已停止 / 运行中的作业,以及它们的作业号和作业中使用的命令。 

          查看作业的 PID,可以在 jobs 命令中加入 -l 选项

       -------------------------------------------------------------------------------------------------------------------------------------------------------

                                          jobs 命令参数

                           参  数                                           描  述

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

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

                              -p                             只列出作业的 PID

                              -r                              只列出运行中的作业

                              -s                              只列出已停止的作业 

        -------------------------------------------------------------------------------------------------------------------------------------------------------

        你可能注意到了 jobs 命令输出中的加号和减号。带加号的作业会被当做默认作业。在使用作业控制命令时,如果未在命令行指定任何作业号,该作业会被当成作业控制命令的操作对象。
        当前的默认作业完成处理后,带减号的作业成为下一个默认作业。任何时候都只有一个带加 号的作业和一个带减号的作业,不管 shell 中有多少个正在运行的作业。

        下面例子说明了队列中的下一个作业在默认作业移除时是如何成为默认作业的。有 3 个独立 的进程在后台被启动。jobs 命令显示出了这些进程、进程的 PID 及其状态。注意,默认进程(带 有加号的那个)是最后启动的那个进程,也就是 3 号作业。 
        

       调用 kill 命令向默认进程发送了一个 SIGHUP 信号,终止了该作业。在接下来的 jobs 命令输出中,先前带有减号的作业成了现在的默认作业,减号也变成了加号。

     2.3.2  重启停止的作业

         在 bash 作业控制中,可以将已停止的作业作为后台进程或前台进程重启。前台进程会接管你当前工作的终端( 交互式进程会占用终端 ),所以在使用该功能时要小心了。

        要以后台模式重启一个作业,可用 bg 命令加上作业号。

         

         bg 命令将作业置于后台模式。注意,当使用了 jobs 命令时,它列出了作业及其状态,即便是默认作业当前并未处于后台模式   (由上图可以看出,使用 bg 命令之后脚本运行是紧接着以上开始,而不是从头开始运行)

         要以前台模式重启作业,可用带有作业号的 fg 命令。

      2.4  调整谦让度

         在多任务操作系统中(Linux 就是),内核负责将 CPU 时间分配给系统上运行的每个进程。调 度优先级(scheduling priority)是内核分配给进程的 CPU 时间(相对于其他进程)。在 Linux 系统 中,由 shell 启动的所有进程的调度优先级默认都是相同的。 调度优先级是个整数值,从 - 20(最高优先级)到 + 19(最低优先级)。默认情况下,bash shell 以优先级 0 来启动所有进程。

      -------------------------------------------------------------------------------------------------------------------------------------------------------

       窍门 最低值 - 20 是最高优先级,而最高值 19 是最低优先级,这太容易记混了。只要记住那句俗 语 “好人难做” 就行了。越是 “好” 或高的值,获得 CPU 时间的机会越低。

       -------------------------------------------------------------------------------------------------------------------------------------------------------

       2.4.1  nice 命令 ( 用作升高 NI 值,即降低优先级 )( 只有一个选项 - n )

          nice 命令允许设置命令启动时的调度优先级。要让命令以更低的优先级运行,只要用 nice 的 - n 命令行来指定新的优先级级别。 

          UID 是用户 ID,PID 是进程 ID,PPID 是父进程 ID

         

         注意,必须将 nice 命令和要启动的命令放在同一行中。ps 命令的输出验证了谦让度值(NI 列)已经被调整到了 10

         nice 命令也可以直接在破折号跟优先级值,例如  nice  -10   ……

       2.5.2  renice 命令

           renice 允许通过运行进程的 PID 来改变它的优先级,nice 是通过进程名

           

           renice 命令会自动更新当前运行进程的调度优先级。

          和 nice 命令一样,renice 命令也有一 些限制:

                只能对属于用户本身的进程执行 renice;

                只能通过 renice 降低进程的优先级;

                root 用户可以通过 renice 来任意调整进程的优先级。

         如果想完全控制运行进程,必须以 root 账户身份登录或使用 sudo 命令。 

       2.6  定时运行作业

         2.6.1  用 at 命令来计划执行作业 ( 只会执行一次 )

              at 命令允许指定 Linux 系统何时运行脚本。at 命令会将作业提交到队列中,指定 shell 何时运 行该作业。at 的守护进程 atd 会以后台模式运行,检查作业队列来运行作业。大多数 Linux 发行 版会在启动时运行此守护进程。

              atd 守护进程会检查系统上的一个特殊目录(通常位于 / var/spool/at)来获取用 at 命令提交的作业。默认情况下,atd 守护进程会每 60 秒检查一下这个目录。有作业时,atd 守护进程会检查 作业设置运行的时间。如果时间跟当前时间匹配,atd 守护进程就会运行此作业

             1. at 命令的格式

                 at 命令的基本格式非常简单: 
                               at [-f filename] time              

选项:    -m     当计划任务结束后发送邮件给用户

              -l        查看用户计划任务

               -d  (跟任务编号) 删除用户计划任务

               -c       查看 at 任务具体内容

            

           

             默认情况下,at 命令会将 STDIN 的输入放到队列中。你可以用 - f 参数来指定用于读取命令(脚本文件)的文件名。

             time 参数指定了 Linux 系统何时运行该作业。如果你指定的时间已经错过,at 命令会在第二天的那个时间运行指定的作业。

            在如何指定时间这个问题上,非常灵活。at 命令能识别多种不同的时间格式。

                  标准的小时和分钟格式,比如 10:15。

                  AM/PM 指示符,比如 10:15 PM。

                  特定可命名时间,比如 now、noon、midnight 或者 teatime(4 PM)。

                 除了指定运行作业的时间,也可以通过不同的日期格式指定特定的日期。

                  标准日期格式,比如 MMDDYY、MM/DD/YY 或 DD.MM.YY。

                  文本日期,比如 Jul 4 或 Dec 25,加不加年份均可。

                  你也可以指定时间增量。

                            当前时间 + 25 min

                            明天 10:15 PM

                            10:15+7 天

              在你使用 at 命令时,该作业会被提交到作业队列(job queue)。作业队列会保存通过 at 命令 提交的待处理的作业。针对不同优先级,存在 26 种不同的作业队列。作业队列通常用小写字母 a~z 和大写字母 A~Z 来指代。 

           -------------------------------------------------------------------------------------------------------------------------------------------------------

          作业队列的字母排序越高,作业运行的优先级就越低(更高的 nice 值)。默认情况下,at 的作业会被提交到 a 作业队列。如果想以更高优先级运行作业,可以用 - q 参数指定不同的队列字母

          --------------------------------------------------------------------------------------------------------------------------------------------------------

             2. 获取作业的输出

                 at 命令利用 sendmail 应用程序来发送邮件。如 果你的系统中没有安装 sendmail,那就无法获得任何输出!因此在使用 at 命令时,最好在脚本 中对 STDOUT 和 STDERR 进行重定向

                -M 可以屏蔽作业产生的输出信息

             3. 列出等待的作业

                 atq 命令可以查看系统中有哪些作业在等待( 第一列排序越高优先级越低 )

                 

             4.  删除作业

                  一旦知道了哪些作业在作业队列中等待,就能用 atrm 命令来删除等待中的作业

                  

          2.7 安排需要定期执行的脚本( 周期执行 )

                   Linux 系统使用 cron 程序来安排要定期执行的作业。cron 程序会在后台运行并检查一个特殊的 表(被称作 cron 时间表),以获知已安排执行的作业。 

          2.7.1 使用 cron 制定计划任务之前需要确保 crond 服务是开启的,否则计划任务不会被执行

            

用法:    crontab  [-u 用户] [-l | -r | -e ]

选项:       -u            指定计划任务的用户,默认为当前用户

**               -l             查看计划任务**

**               -r            删除计划任务**

**               -e            编辑计划任务**

**               -i             使用 - r 删除计划任务时要求用户确认删除**

     

           例:(cron 文件的编辑器是 vim)

                $crontab  -e

                 


说明

      如何设置一个在每个月的最后一天执行的命令呢?因为你无法设置 dayofmonth 的值来涵盖所有的月份。这个问题困扰着 Linux 和 Unix 程序员,也激发了不少解 决办法。常用的方法是加一条使用 date 命令的 if-then 语句来检查明天的日期是不是 01:

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

      它会在每天中午 12 点来检查是不是当月的最后一天,如果是,cron 将会运行该命令。 


       

            命令列表必须指定要运行的命令或脚本的全路径名。你可以像在普通的命令行中那样,添加 任何想要的命令行参数和重定向符号。 
                15 10 * * * /home/rich/test4.sh > test4out

           cron 程序会用提交作业的用户账户运行该脚本。因此,你必须有访问该命令和命令中指定的 输出文件的权限。

         2.7.2  构建 cron 时间表 

             每个系统用户(包括 root 用户)都可以用自己的 cron 时间表来运行安排好的任务。Linux 提供 了 crontab 命令来处理 cron 时间表。要列出已有的 cron 时间表,可以用 - l 选项。 

           默认情况下,用户的 cron 时间表文件并不存在。要为 cron 时间表添加条目,可以用 -e 选项。 在添加条目时,crontab 命令会启用文本编辑器 vim,使用已有的 cron 时间表作为文件内容(或者是一个空文件,如果时间表不存在的话)。  

         2.7.3  浏览 cron 目录

           如果你创建的脚本对精确的执行时间要求不高,用预配置的 cron 脚本目录会更方便。有 4 个基本目录:hourly、daily、monthly 和 weekly

             

           因此,如果脚本需要每天运行一次,只要将脚本复制到 daily 目录,cron 就会每天执行它。 

         2.7.4  anacron 程序

              cron 程序的唯一问题是它假定 Linux 系统是 7×24 小时运行的。除非将 Linux 当成服务器环境来 运行,否则此假设未必成立。

            如果某个作业在 cron 时间表中安排运行的时间已到,但这时候 Linux 系统处于关机状态,那么 这个作业就不会被运行。当系统开机时,cron 程序不会再去运行那些错过的作业。要解决这个问 题,许多 Linux 发行版还包含了 anacron 程序。

           如果 anacron 知道某个作业错过了执行时间,它会尽快运行该作业。这意味着如果 Linux 系统 关机了几天,当它再次开机时,原定在关机期间运行的作业会自动运行。

          这个功能常用于进行常规日志维护的脚本。如果系统在脚本应该运行的时间刚好关机, 日志文件就不会被整理,可能会变很大。通过 anacron,至少可以保证系统每次启动时整理日志文件。

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

                  

           anacron 程序使用自己的时间表(通常位于 / etc/anacrontab)来检查作业目录                                                                                

        anacron 时间表的基本格式和 cron 时间表略有不同: 
                         period delay identifier command

               period 条目定义了作业多久运行一次,以天为单位。anacron 程序用此条目来检查作业的时间 戳文件。delay 条目会指定系统启动后 anacron 程序需要等待多少分钟再开始运行错过的脚本。 command 条目包含了 run-parts 程序和一个 cron 脚本目录名。run-parts 程序负责运行目录中传给它的 任何脚本。

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

               这是因为 anacron 程序不会处理执行时间 需求小于一天的脚本。

               identifier 条目是一种特别的非空字符串,如 cron-weekly。它用于唯一标识日志消息和错误 邮件中的作业。