在调查关键字stderr
重定向问题和问题的标准解决方案时,我遇到了另一个问题。描述给出了bash
time
man bash
(list)
复合命令是list 在子shell环境中执行。现在,正如您从小程序的执行中看到的那样hello
- 它进行了简单的调用,看起来命令行和命令行system("ps -opid,ppid,comm")
之间没有任何区别。这让我感到惊讶。不过我知道当命令是内置命令时会有区别。在这种情况下,如果没有括号,则不会发生分叉,但如果内置命令周围有括号,则会发生分叉。我错过了什么吗?(./hello)
./hello
18:59:13 -> echo $$
5323
19:34:30 -> ./hello
hello world from process ID 8657, parent ID 5323
PID PPID COMM
5323 5322 -bash
8657 5323 ./hello
19:42:34 -> (./hello)
hello world from process ID 8977, parent ID 5323
PID PPID COMM
5323 5322 -bash
8977 5323 ./hello
19:42:43 -> time (./hello)
hello world from process ID 8985, parent ID 8984
PID PPID COMM
5323 5322 -bash
8984 5323 -bash
8985 8984 ./hello
real 0m0.021s
user 0m0.003s
sys 0m0.004s
19:42:53 -> (time ./hello)
hello world from process ID 9000, parent ID 8999
PID PPID COMM
5323 5322 -bash
8999 5323 -bash
9000 8999 ./hello
real 0m0.030s
user 0m0.003s
sys 0m0.005s
19:43:05 ->
答案1
不保证子 shell 在单独的进程中运行。这只是通常的实现。
子 shell 的定义是 shell 必须确保子 shell 不会更改其父 shell 的状态:它不能影响父 shell 的变量、定义的函数、别名、选项、历史记录、限制等。最简单的方法是是在单独的进程中运行子 shell,所以通常会发生这种情况。但是,如果 shell 可以确保子 shell 不会影响父 shell 中的任何内容,则它不必运行单独的进程。
对于大多数 sh 实现,除了运行外部命令之外不执行任何操作的子 shell 不会分叉两次。
for sh in dash bash ksh93 mksh zsh; do printf "%6s " "$sh"; "$sh" -c '(perl -e "print getppid()"); echo " $$"'; done
dash 945 945
bash 947 947
ksh93 949 949
mksh 951 951
zsh 953 953
一旦您执行了更复杂的操作,bash 就会分叉子 shell,并再次分叉外部命令。其他一些 shell 不会分叉(子)shell 的最后一个命令(除非有陷阱)。
for sh in dash bash ksh93 mksh zsh; do printf "%6s " "$sh"; "$sh" -c '(true; perl -e "print getppid()"); echo " $$"'; done
dash 1306 1306
bash 1309 1308
ksh93 1311 1311
mksh 1314 1313
zsh 1316 1316
ATT ksh ( ksh93
) 比其他 shell 优化更多。在我进行比较的五个程序中,它是唯一一个通过在同一进程中创建单独的数据结构来跟踪子 shell 中不断变化的数据来进行优化的程序。
for sh in dash bash ksh93 mksh zsh; do printf "%6s " "$sh"; "$sh" -c '(perl -e "print getppid()"; /bin/true); echo " $$"'; done
dash 3082 3081
bash 3085 3084
ksh93 3088 3088
mksh 3092 3091
zsh 3096 3095
有些事情甚至会强制 ATT ksh 为子 shell 创建子进程,例如更改进程限制。
for sh in dash bash ksh93 mksh zsh; do printf "%6s " "$sh"; "$sh" -c '(ulimit -n 42; perl -e "print getppid()"; /bin/true); echo " $$"'; done
dash 4504 4503
bash 4507 4506
ksh93 4511 4510
mksh 4515 4514
zsh 4519 4518