1.进程ID和作业编号
当通过附加&号后运行命令时,shell会响应如下:
$ ls &
[1] 3318
[1] 3318
其中[1]是作业号,3318是进程号。
一个后台进程完成时,shell会给出作业编号信息,如下:
[1]+ Done ls --color=auto
如果作业以非0状态退出时,shell指出其退出状态。
2.作业控制
作业编号可以使它们在shell命令中进行作业控制。一旦作业在后台运行,你可以让它一直运行,或把它放到前台,或向其发送信号。
2.1.前台和后台
内置fg命令将后台作业放到前台。使用不带参数的fg,shell会把后台作业放到前台,如果有多个作业在后台,shell会挑出最新在后台
运行作业放到前台。如果想要将其他作业放到前台,需要给出前面加上百分号%的作业命令,或者使用作业编号前面加百分号%,也
可以是不带百分号的进程ID。可以使用命令jobs列出后台作业。
例如:
yanwenjie@ywjpc:~/ctest$ ./a &
[1] 3481
yanwenjie@ywjpc:~/ctest$ ./b &
[2] 3482
yanwenjie@ywjpc:~/ctest$ ./c &
[3] 3483
yanwenjie@ywjpc:~/ctest$ jobs
[1] Running ./a &
[2]- Running ./b &
[3]+ Running ./c &
[1] 3481
yanwenjie@ywjpc:~/ctest$ ./b &
[2] 3482
yanwenjie@ywjpc:~/ctest$ ./c &
[3] 3483
yanwenjie@ywjpc:~/ctest$ jobs
[1] Running ./a &
[2]- Running ./b &
[3]+ Running ./c &
-p选项只列出进程号:
$ jobs -p
3481
3482
3483
$ jobs -p
3481
3482
3483
如果键入fg,会把c放到前台,因为它是最新在后台运行的作业。
如果键入fg %b,或者fg %2,b会进入前台。
还可以通过%+引用被放到后台的最新作业,通过%-引用下一个最近被放到后台的作业。这边是b。
下面列出了引用后台作业的几种方式:
%N 作业编号N
%string 其命令以string开始的作业
%?string 其命令包含string的作业
%+ 最近被调用的后台作业
%% 同上
%- 第二个最近被调用的后台程序
2.2.挂起一个作业
要挂起一个作业,在其运行时键入ctrl-z即可。shell会相应如下消息:
$ ./a
^Z
[1]+ Stopped ./a
^Z
[1]+ Stopped ./a
然后返回shell提示符,要恢复一个挂机的作业使其继续在前台执行,键入fg即可。如果有多个挂起的作业,可以使用带有一个作
业名或者编号的fg。
yanwenjie@ywjpc:~/ctest$ jobs
[1] Stopped ./a
[2]- Stopped ./b
[3]+ Stopped ./c
yanwenjie@ywjpc:~/ctest$ fg %1
./a
如果键入ctrl-z后跟bg,就会把该作业放到后台运行。
yanwenjie@ywjpc:~/ctest$ jobs
[1] Stopped ./a
[2]- Stopped ./b
[3]+ Stopped ./c
yanwenjie@ywjpc:~/ctest$ bg %2
[2]- ./b &
yanwenjie@ywjpc:~/ctest$ jobs
[1]- Stopped ./a
[2] Running ./b &
[3]+ Stopped ./c
业名或者编号的fg。
yanwenjie@ywjpc:~/ctest$ jobs
[1] Stopped ./a
[2]- Stopped ./b
[3]+ Stopped ./c
yanwenjie@ywjpc:~/ctest$ fg %1
./a
如果键入ctrl-z后跟bg,就会把该作业放到后台运行。
yanwenjie@ywjpc:~/ctest$ jobs
[1] Stopped ./a
[2]- Stopped ./b
[3]+ Stopped ./c
yanwenjie@ywjpc:~/ctest$ bg %2
[2]- ./b &
yanwenjie@ywjpc:~/ctest$ jobs
[1]- Stopped ./a
[2] Running ./b &
[3]+ Stopped ./c
3.信号
3.1.控制键信号
键入ctrl-c时,shell发送INT信号给当前作业,键入ctrl-z时,shell则发送TSTP。也可以向当前作业发送一个QUIT信号,方法是键入
ctrl-\。
可以使用stty命令选项定制发送信号的控制键。这一点随系统的不同而变化,通常的语法是:stty signame char。signame时信号
名,char是控制字符,可通过使用^符号表示控制后跟控制字符给出。例如,要将INT键设置为大多数系统上的ctrl-x,可使用:
stty intr ^x。
3.2.kill
可以使用内置shell命令kill向你创建的任何进程发送一个信号,kill的参数为进程ID,作业编号。
默认情况下,kill发送TERM信号,其效果与使用ctrl-c发送的INT信号一样。
下面是kill的例子。这里有一个a进程,进程号是2680,作业号时1,开始可以使用如下命令:
# ./a &
[1] 2680
# ./a &
[1] 2680
# kill %1
[1]+ Terminated ./a
如果没有看到该消息,TERM信号中断作业失败,下一步再试试QUIT:
kill -QUIT %1
如果工作正常会看到:
[1]+ Quit (core dumped) ./a
如果QUIT也不正常运行,自后一种方式是使用KILL:
# kill -KILL %1
[1]+ Killed ./a
3.3.trap
trap内置命令使你可以设置为捕获特定信号并以自己的方式处理它们。trap内置命令使你可以在一个shell脚本中完成此功能。
trap的语法是:
trap cmd sig1 sig2 ...
意思是 sig1,sig2等被接收时,运行cmd,然后恢复执行,cmd完成后,脚本在被打断的命令后恢复执行。cmd可以为脚本或者
函数。sigs可用名称或数字指定。
例如:
trap "echo 'you hit ctrl-c'" INT
while true; do
sleep 60
done
while true; do
sleep 60
done
执行:
# ./a.sh
^Cyou hit ctrl-c
^Cyou hit ctrl-c
^Cyou hit ctrl-c
^Cyou hit ctrl-c
按下ctrl-c后,脚本不会停止运行,而是sleep命令退出,脚本会循环回来启动另一个sleep。
3.4.进程ID变量
$$是一个特殊shell变量,取值为当前shell的进程ID。
例如如下脚本:
echo $$
while true; do
sleep 10
done
while true; do
sleep 10
done
执行结果:
root@ywjpc:/home/yanwenjie/bashtest# ./a.sh &
[1] 3258
root@ywjpc:/home/yanwenjie/bashtest# 3258
[1] 3258
root@ywjpc:/home/yanwenjie/bashtest# 3258
3.5.重置陷阱信号
另一个trap命令的特例发生在将短划线指定为命令参数时。它会将收到信号时的行为重置为默认欣慰,通常是进程的终止。
例如a.sh如下所示:
$ cat a.sh
trap "echo 'ctrl c is received'" INT
i=5
while [ $i -gt 0 ]; do
sleep 5
i=$((i-1))
done
trap - INT
i=5
while [ $i -gt 0 ]; do
sleep 5
i=$((i-1))
done
trap "echo 'ctrl c is received'" INT
i=5
while [ $i -gt 0 ]; do
sleep 5
i=$((i-1))
done
trap - INT
i=5
while [ $i -gt 0 ]; do
sleep 5
i=$((i-1))
done
执行脚本:
$ ./a.sh
^Cctrl c is received
^Cctrl c is received
^Cctrl c is received
^Cctrl c is received
^Cctrl c is received
^C
^Cctrl c is received
^Cctrl c is received
^Cctrl c is received
^Cctrl c is received
^Cctrl c is received
^C
4.协同程序
例如下面的脚本:
alice &
alice &
hatter
此时hatter时脚本中最后一个命令,上述代码只有当alice首先完成时,才能工作正常。如果当脚本完成时,alice仍然运行,那么
它就变成孤儿。
有一种方法可以确保alice完成前脚本不会完成:内置命令wait。不带参数时,wait指示等待,直至所有后台作业完成,因此要确保
上述代码工作正常,加入wait如下:
alice &
hatter
wait
这里,如果hatter先完成, 父shell在结束自己前会等待alice完成。
5.子shell
5.1.子shell继承
关于子shell最关键的一点是它们从其父shell获得或继承了如下特性:
- 当前目录
- 环境变量
- 标准输入,标准输出和标准错误,以及其它任何打开的文件描述符。
- 被忽略的信号。
子shell未从其父shell继承的内容如下:
- shell变量
- 没有被忽略的信号处理
5.2.嵌套子shell
子shell不需要放在单独的脚本中,你也可以在与父shell相同的脚本中启动子shell。可以把某些shell代码放到圆括号中,则该代码
将运行在子shell。我们称之为嵌套子shell。例如:
( while read line; do
echo $line
done
) | dc
圆括号内代码会运行为一个单独的进程。这通常不如一个命令块效率高。子shell和命令块在功能上的差别很少;它们之间的主要区别
是作用域;亦即在该范围内一些定义是已知的,如shell变量和信号陷阱。首先,嵌套子shell内的代码服从上述子shell继承规则,除此
之外还直到外部shell中定义的变量,块可以看做继承了外部shell的一切内容的代码单元。第二,一个命令块中定义的变量和信号陷阱
对块后的shell代码时已知的,而在子shell中则不是。