三种 shell 脚本调用方法(fork, exec, source)
fork (/path/to/script.sh)
fork 是最普通的,就是直接在脚本里面用 /directory/script.sh 来调用 script.sh 这个脚本。
运行的时候开一个 sub-shell 执行调用的脚本,sub-shell 执行的时候,parent-shell 还在但会阻塞。sub-shell 执行完毕后返回 parent-shell。
sub-shell 从 parent-shell 继承环境变量,但是 sub-shell 中的环境变量不会带回 parent-shell。
exec (exec /path/to/script.sh)
exec 与 fork 不同,不需要新开一个 sub-shell 来执行被调用的脚本。被调用的脚本与父脚本在同一个 shell 内执行。但是使用 exec 调用一个新脚本以后, 父脚本中 exec 行之后的内容就不会再执行了。这是 exec 和 source 的区别。
source (source /directory/script.sh)
与 fork 的区别是不新开一个 sub-shell 来执行被调用的脚本,而是在同一个 shell 中执行。所以被调用的脚本中声明的变量和环境变量。都可以在主脚本中得到和使用。
因此我们可以通过 source 运行一个脚本,通过该脚本来设置环境变量。
在 bash 中如何让程序后台运行
非后台运行
我们在 bash 中直接运行程序时,如
$ sleep 5
就是先 fork 出一个 sub-shell,在 sub-shell 中用 exec 调用目标程序,因此目标程序执行完后,sub-shell 也会结束退出,再返回到 parent-shell 中。目标程序执行过程中 parent-shell 是阻塞的。
后台运行
如何在 bash 中让程序在后台运行而不阻塞当前 shell 呢,只需要在命令后加上&
即可,如
$ sleep 5 &
对于这样的命令,系统会创建一个 sub-shell 来运行。同时,在 parent-shell 中会立刻返回 0 并且继续下面的命令执行。除此之外,在执行这个命令后,terminal 上会输出创建的 sub-shell 的进程 ID(PID)。
$ sleep 5 &
[1] 618970
注意:按照这种方法分支出去的 sub-shell 的 stdout 会仍然关联到其 parent-shell,也就是说你在当前的 terminal 中仍然可以看到这个后台进程的 stdout 输出。下面会介绍重定向输出的方法。
如果需要在 sub-shell 中执行多条命令,可以使用()
包裹这些命令,如
(./myscript1.py
./myscript2.py
./myscript3.py) &
查看后台进程
通过&
分支出去的 sub-shell 的 PID 被存储在一个特殊的变量$!
中
$ echo $!
618970
同时,你也可以通过jobs
命令来检查 sub-shell 的信息
$ jobs
[1] + running sleep 5
对于 sub-shell,你可以通过fg
命令将其拉回当前的 terminal。
进程输出重定向
有时候在 shell 命令中可能看到这样的写法,将程序输出(包括标准输出和错误输出)重定向到 /dev/null,也就是省略输出
some_command > /dev/null 2>&1
1 是系统默认代表 stdout 的值,2 代表 stderr
前面的> /dev/null
好理解,> 是 1> 的简写,代表将 stdout 导向到 /dev/null
而后面的2>&1
代表将 stderr 重定向到 stdout。由于 stdout 已经被重定向到 /dev/null,那么这意味着 stderr 也被重定向到了这个位置。