高级Bash脚本编程指南(21):作业控制命令
成于坚持,败于止步
在下面的学习作业控制命令的过程中,你会不止一次的碰到一些标识符,这里叫做“作业标识符”,首先列出这是标识符
记法 | 含义 |
---|---|
%N | 作业号[N] |
%S | 以字符串S开头的被(命令行)调用的作业 |
%?S | 包含字符串S的被(命令行)调用的作业 |
%% | "当前"作业(前台最后结束的作业, 或后台最后启动的作业) |
%+ | "当前"作业(前台最后结束的作业, 或后台最后启动的作业) |
%- | 最后的作业 |
$! | 最后的后台进程 |
jobs
在后台列出所有正在运行的作业, 给出作业号. 并不象ps命令那么有用.
作业和进程的概念太容易混淆了. 特定的内建命令, 比如kill, disown, 和wait命令,既可以接受作业号为参数, 也可以接受进程号为参数. 但是fg, bg和jobs命令就只能接受作业号为参数.
root@haitao-VirtualBox:~# sleep 100 &
[1] 14884
root@haitao-VirtualBox:~# jobs
[1]+ Running sleep 100 &
root@haitao-VirtualBox:~#
"1"是作业号(作业是被当前shell所维护的), 而"14884"是进程号(进程是被系统维护的). 为了kill掉作业/进程, 或者使用kill %1或者使用kill 1384. 这两个命令都行.
disown
从shell的激活作业表中删除作业.
fg, bg
fg命令可以把一个在后台运行的作业放到前台来运行. 而bg命令将会重新启动一个挂起的作业, 并且在后台运行它. 如果使用fg或者bg命令的时候没有指定作业号, 那么默认将对当前正在运行的作业进行操作.
wait
停止脚本的运行, 直到后台运行的所有作业都结束为止, 或者如果传递了作业号或进程号为参数的话, 那么就直到指定作业结束为止. 返回等待命令的退出状态码.
你可以使用wait命令来防止在后台作业没完成(这会产生一个孤儿进程)之前退出脚本.
在继续处理之前, 等待一个进程的结束的实例
#!/bin/bash
ROOT_UID=0 # 只有$UID为0的用户才拥有root权限.
E_NOTROOT=65
E_NOPARAMS=66
if [ "$UID" -ne "$ROOT_UID" ]
then
echo "Must be root to run this script."
# "Run along kid, it's past your bedtime."
exit $E_NOTROOT
fi
if [ -z "$1" ]
then
echo "Usage: `basename $0` find-string"
exit $E_NOPARAMS
fi
echo "Updating 'locate' database..."
echo "This may take a while."
updatedb /usr & # 必须使用root身份来运行.
wait
# 将不会继续向下运行, 除非'updatedb'命令执行完成.
# 你希望在查找文件名之前更新database.
locate $1
# 如果没有'wait'命令的话, 而且在比较糟的情况下,
#+ 脚本可能在'updatedb'命令还在运行的时候退出,
#+ 这将会导致'updatedb'成为一个孤儿进程.
exit 0
可选的, wait也可以接受一个作业标识符作为参数, 比如, wait%1或者wait $PPID.
在一个脚本中, 使用后台运行命令(&)可能会使这个脚本挂起, 直到敲ENTER, 挂起的脚本才会被恢复. 看起来只有在这个命令的结果需要输出到stdout的时候, 这种现象才会出现. 这是个很烦人的现象.
#!/bin/bash
# test.sh
ls -l &
echo "Done."
结果:
root@haitao-VirtualBox:~/test# chmod +x test1.sh
root@haitao-VirtualBox:~/test# ls
test1.sh
root@haitao-VirtualBox:~/test# ./test1.sh
Done.
root@haitao-VirtualBox:~/test# total 4
-rwxr-xr-x 1 root root 46 2013-06-17 18:43 test1.sh
root@haitao-VirtualBox:~/test#
看起来只要在后台运行命令的后边加上一个wait命令就会解决这个问题.
#!/bin/bash
# test.sh
ls -l &
wait
echo "Done."
结果:
root@haitao-VirtualBox:~/test# ./test1.sh
total 4
-rwxr-xr-x 1 root root 51 2013-06-17 18:44 test1.sh
Done.
root@haitao-VirtualBox:~/test#
如果将后台运行命令的输出重定向到文件中或/dev/null中, 也能解决这个问题.这里原理其实是一样的
suspend
这个命令的效果与Control-Z很相像, 但是它挂起的是这个shell(这个shell的父进程应该在合适的时候重新恢复它).
logout
退出一个已经登陆上的shell, 也可以指定一个退出状态码.
times
给出执行命令所占用的时间, 使用如下的形式进行输出:
0m0.020s 0m0.020s
这只能给出一个很有限的值, 因为它很少在shell脚本中出现.
kill
通过发送一个适当的结束信号, 来强制结束一个进程
一个结束自身的脚本程序
#!/bin/bash
kill $$ # 脚本将在此处结束自己的进程.
# 回忆一下,"$$"就是脚本的PID.
echo "This line will not echo."
# 而且shell将会发送一个"Terminated"消息到stdout.
exit 0
结果:
root@haitao-VirtualBox:~/test# chmod +x test2.sh
root@haitao-VirtualBox:~/test# ./test2.sh
Terminated
root@haitao-VirtualBox:~/
kill -l将会列出所有信号. kill -9是"必杀"命令, 这个命令将会结束顽固的不想被kill掉的进程. 有时候kill -15也能干这个活. 一个"僵尸进程", 僵尸进程就是子进程已经结束了, 但是父进程还没kill掉这个子进程, 不能被登陆的用户kill掉 -- 因为你不能杀掉一些已经死了的东西 -- 但是init进程迟早会把它清除干净.
killall
killall命令将会通过名字来杀掉一个正在运行的进程, 而不是通过进程ID. 如果某个特定的命令有多个实例正在运行, 那么执行一次killall命令就会把这些实例全部杀掉.这里所指的killall命令是在/usr/bin中, 而不是/etc/rc.d/init.d中的killall脚本.
command
对于命令"COMMAND", command COMMAND会直接禁用别名和函数的查找.注意一下Bash执行命令的优先级:
1 别名
2 关键字
3 函数
4 内建命令
5 脚本或可执行程序($PATH)
这是shell用来影响脚本命令处理效果的三个命令之一. 另外两个分别是builtin和enable. (译者注: 当你想运行的命令或函数与内建命令同名时, 由于内建命令比外部命令的优先级高, 而函数比内建命令的优先级高, 所以Bash将总会执行优先级比较高的命令. 这样当你想执行优先级低的命令的时候, 就没有选择的余地了. 这三个命令就是用来为你提供这样的机会. )
builtin
当你使用builtin BUILTIN_COMMAND的时候, 只会调用shell内建命令"BUILTIN_COMMAND", 而暂时禁用同名的函数, 或者是同名的扩展命令.
enable
这个命令或者禁用内建命令或者恢复内建命令. 比如, enable -n kill将禁用内建命令kill, 所以当我们调用kill命令时, 使用的将是/bin/kill外部命令.
-a选项会enable所有作为参数的shell内建命令, 不管它们之前是否被enable了.
(译者注: 如果不带参数的调用enable -a, 那么会恢复所有内建命令.) -f filename选项将会从适当的编译过的目标文件中, 让enable命令以共享库的形式来加载内建命令.
autoload
这是从ksh中的autoloader命令移植过来的. 一个带有"autoload"声明的函数, 在它第一次被调用的时候才会被加载.这样做是为了节省系统资源.
注意, autoload命令并不是Bash核心安装时候的一部分. 这个命令需要使用命令enable -f来加载(参考上边的enable命令).
先到这里了,O(∩_∩)O~
我的专栏地址:http://blog.csdn.net/column/details/shell-daily-study.html
待续。。。。