11.1. 作业控制命令

下边的作业控制命令需要一个"作业标识符"作为参数. 请参考本章结尾部分的表格.

jobs

在后台列出所有正在运行的作业, 给出作业号. 并不象ps命令那么有用.

Note

作业进程的概念太容易混淆了. 特定的内建命令, 比如kill, disown, 和wait命令即可以接受作业号为参数, 也可以接受进程号为参数. 但是fg, bgjobs命令就只能接受作业号为参数.

bash$ sleep 100 &
[1] 1384

bash $ jobs
[1]+  Running                 sleep 100 &

"1"是作业号(作业是被当前shell所维护的), 而"1384"是进程号(进程是被系统维护的). 为了kill掉作业/进程, 或者使用kill %1或者使用kill 1384. 这两个命令都行.

感谢, S.C.

disown

从shell的激活作业表中删除作业.

fg, bg

fg命令可以把一个在后台运行的作业放到前台来运行. 而bg命令将会重新启动一个挂起的作业, 并且在后台运行它. 如果使用fg或者bg命令的时候没有指定作业号, 那么默认将对当前正在运行的作业进行操作.

wait

停止脚本的运行, 直到后台运行的所有作业都结束为止, 或者如果传递了作业号或进程号为参数的话, 那么就直到指定作业结束为止. 返回等待命令的退出状态码.

你可以使用wait命令来防止在后台作业没完成(这会产生一个孤儿进程)之前退出脚本.


例子 11-25. 在继续处理之前, 等待一个进程的结束

  1 #!/bin/bash
  2 
  3 ROOT_UID=0   # 只有$UID为0的用户才拥有root权限.
  4 E_NOTROOT=65
  5 E_NOPARAMS=66
  6 
  7 if [ "$UID" -ne "$ROOT_UID" ]
  8 then
  9   echo "Must be root to run this script."
 10   # "Run along kid, it's past your bedtime."
 11   exit $E_NOTROOT
 12 fi  
 13 
 14 if [ -z "$1" ]
 15 then
 16   echo "Usage: `basename $0` find-string"
 17   exit $E_NOPARAMS
 18 fi
 19 
 20 
 21 echo "Updating 'locate' database..."
 22 echo "This may take a while."
 23 updatedb /usr &     # 必须使用root身份来运行.
 24 
 25 wait
 26 # 将不会继续向下运行, 除非'updatedb'命令执行完成.
 27 # 你希望在查找文件名之前更新database.
 28 
 29 locate $1
 30 
 31 #  如果没有'wait'命令的话, 而且在比较糟的情况下,
 32 #+ 脚本可能在'updatedb'命令还在运行的时候退出,
 33 #+ 这将会导致'updatedb'成为一个孤儿进程.
 34 
 35 exit 0

可选的, wait也可以接受一个作业标识符作为参数, 比如, wait%1或者wait $PPID. 请参考作业标识符表.

Tip

在一个脚本中, 使用后台运行命令(&)可能会使这个脚本挂起, 直到敲ENTER, 挂起的脚本才会被恢复. 看起来只有在这个命令的结果需要输出到stdout的时候, 这种现象才会出现. 这是个很烦人的现象.
  1 #!/bin/bash
  2 # test.sh		  
  3 
  4 ls -l &
  5 echo "Done."
bash$ ./test.sh
Done.
 [bozo@localhost test-scripts]$ total 1
 -rwxr-xr-x    1 bozo     bozo           34 Oct 11 15:09 test.sh
 _
               

看起来只要在后台运行命令的后边加上一个wait命令就会解决这个问题.
  1 #!/bin/bash
  2 # test.sh		  
  3 
  4 ls -l &
  5 echo "Done."
  6 wait
bash$ ./test.sh
Done.
 [bozo@localhost test-scripts]$ total 1
 -rwxr-xr-x    1 bozo     bozo           34 Oct 11 15:09 test.sh
               
如果将后台运行命令的输出重定向到文件中或/dev/null中, 也能解决这个问题.

suspend

这个命令的效果与Control-Z很相像, 但是它挂起的是这个shell(这个shell的父进程应该在合适的时候重新恢复它).

logout

退出一个已经登陆上的shell, 也可以指定一个退出状态码.

times

给出执行命令所占用的时间, 使用如下的形式进行输出:
0m0.020s 0m0.020s
这只能给出一个很有限的值, 因为它很少在shell脚本中出现.

kill

通过发送一个适当的结束信号, 来强制结束一个进程(请参考例子 13-6).


例子 11-26. 一个结束自身的脚本程序

  1 #!/bin/bash
  2 # self-destruct.sh
  3 
  4 kill $$  # 脚本将在此处结束自己的进程.
  5          # 回忆一下,"$$"就是脚本的PID.
  6 
  7 echo "This line will not echo."
  8 # 而且shell将会发送一个"Terminated"消息到stdout.
  9 
 10 exit 0
 11 
 12 #  在脚本结束自身进程之后,
 13 #+ 它返回的退出码是什么?
 14 #
 15 # sh self-destruct.sh
 16 # echo $?
 17 # 143
 18 #
 19 # 143 = 128 + 15
 20 #             结束信号

Note

kill -l将会列出所有信号. kill -9"必杀"命令, 这个命令将会结束顽固的不想被kill掉的进程. 有时候kill -15也能干这个活. 一个"僵尸进程", 僵尸进程就是子进程已经结束了, 但是父进程还没kill掉这个子进程, 不能被登陆的用户kill掉 -- 因为你不能杀掉一些已经死了的东西 -- 但是init进程迟早会把它清除干净.

killall

killall命令将会通过名字来杀掉一个正在运行的进程, 而不是通过进程ID. 如果某个特定的命令有多个实例正在运行, 那么执行一次killall命令就会把这些实例全部杀掉.

Note

这里所指的killall命令是在/usr/bin中, 而不是/etc/rc.d/init.d中的killall脚本.

command

对于命令"COMMAND", command COMMAND会直接禁用别名和函数的查找.

译者注, 注意一下Bash执行命令的优先级:
  1 1	别名
  2 2	关键字
  3 3	函数
  4 4	内建命令
  5 5	脚本或可执行程序($PATH)

Note

这是shell用来影响脚本命令处理效果的三个命令之一. 另外两个分别是builtinenable. (译者注: 当你想运行的命令或函数与内建命令同名时, 由于内建命令比外部命令的优先级高, 而函数比内建命令的优先级高, 所以Bash将总会执行优先级比较高的命令. 这样当你想执行优先级低的命令的时候, 就没有选择的余地了. 这三个命令就是用来为你提供这样的机会. )

builtin

当你使用builtin BUILTIN_COMMAND的时候, 只会调用shell内建命令"BUILTIN_COMMAND", 而暂时禁用同名的函数, 或者是同名的扩展命令.

enable

这个命令或者禁用内建命令或者恢复内建命令. 比如, enable -n kill将禁用内建命令kill, 所以当我们调用kill命令时, 使用的将是/bin/kill外部命令.

-a选项会enable所有作为参数的shell内建命令, 不管它们之前是否被enable了. (译者注: 如果不带参数的调用enable -a, 那么会恢复所有内建命令.) -f filename选项将会从适当的编译过的目标文件 [1] 中, 让enable命令以共享库的形式来加载内建命令.

autoload

这是从ksh中的autoloader命令移植过来的. 一个带有"autoload"声明的函数, 在它第一次被调用的时候才会被加载. [2] 这样做是为了节省系统资源.

注意, autoload命令并不是Bash核心安装时候的一部分. 这个命令需要使用命令enable -f来加载(参考上边的enable命令).


表格 11-1. 作业标识符

记法含义
%N作业号[N]
%S以字符串S开头的被(命令行)调用的作业
%?S包含字符串S的被(命令行)调用的作业
%%"当前"作业(前台最后结束的作业, 或后台最后启动的作业)
%+"当前"作业(前台最后结束的作业, 或后台最后启动的作业)
%-最后的作业
$!最后的后台进程

注意事项

[1]

一些可加载的内建命令的C源代码通常都放在/usr/share/doc/bash-?.??/functions目录下.

注意, enable-f选项并不是所有系统都支持的.

[2]

autoload命令与typeset -fu效果相同.