我们在看别人写的脚步时,你可能会发现别人启动程序时喜欢在命令前面添加一个 exec 命令,它的作用其实是:
将当前的 shell 程序替换为指定的命令或程序,命令或程序不会在新的进程中执行(不会创建新的pid,直接使用当前 shell 的 pid),这意味着原来的 shell 程序将被终止,并且执行的命令将在当前 shell 进程中运行。
这样做的好处在于可以在不创建新的子进程的情况下,直接在当前 shell 进程中执行指定的命令,从而节省了系统资源并且能够更高效地执行命令。另外,在使用 supervise 时通常使用 exec 来保证监控 run 脚本的进程。
注意:
在 exec 这一行命令后面的命令将不会执行,因为新的命令已经替换了当前shell进程,也就是新命令复用了当前 shell 的 pid 以及一些环境。
使用示例
- a.sh 负责打印传给它的第一个参数,同时打印自己的 PID
#!/bin/bash
echo "$1 this is in a.sh my PID:$$"
- x.sh 首先输出自己的PID,然后分别不使用 exec 和使用 exec 去执行 a.sh 脚本
#!/bin/bash
echo "x.sh PID:$$"
bash ./a.sh " not exec"
exec bash ./a.sh "with exec"
echo "line will never be executed."
执行 x.sh 脚本得到如下输出
$ bash ./x.sh
x.sh PID:376124
not exec this is in a.sh my PID:376125
with exec this is in a.sh my PID:376124
PID 376124 是 x.sh 进程的,当没有使用 exec 执行 a.sh 脚本时,创建了一个新的进程 PID 为376125,执行完 a.sh脚本后 376125 进程退出。然后使用 exec 执行 a.sh 脚本,a.sh 会直接在 376124 进程中运行而不会创建新的进程,由于原来的 x.sh进程已经被替换了,所以最后一行 echo 不会被执行。
验证旧的进程被替换
- test1.sh
#!/bin/bash
exec bash ./test2.sh
- test2.sh
#!/bin/bash
sleep 999
- 执行 test1.sh 脚本,然后在另外一个终端使用 ps 查看进程
# 执行 test1.sh
bash test1.sh
# 在另外一个终端查看进程
# 可以看到原来的 test1.sh 已经没有了,只有一个 test2.sh 和 sleep 进程
$ ps aux | grep -E "test|sleep"
yzy 376356 0.0 0.0 7760 3324 pts/0 S+ 18:27 0:00 bash ./test2.sh
yzy 376357 0.0 0.0 6188 1056 pts/0 S+ 18:27 0:00 sleep 999
yzy 376544 0.0 0.0 7004 2104 pts/1 S+ 18:27 0:00 grep --color=auto -E test|sleep