我对 bash 进程不是很熟悉,并且很惊讶地看到在使用管道和tee
.你能帮我理解这一点吗?
在下面,脚本的路径被替换为$FOLDER
.
$FOLDER/DBB/myparent.ksh
:echo "BEGIN $$ this is the parent process" $FOLDER/DBB/myChild.ksh echo "END $$ this is the parent process"
$FOLDER/DBB/mychild.ksh
:function toto { echo " this is $$ child process " sleep 10 } { echo " $$ go1 " toto ptree $$ echo " $$ go2 " } | tee myLog$$.log
当我启动时
myParent.ksh
,我有这个进程树:28417 /usr/lib/ssh/sshd -R 28531 -ksh 41387 /bin/bash myParent.ksh 41390 /bin/bash $FOLDER/DBB/myChild.ksh 41391 /bin/bash $FOLDER/DBB/myChild.ksh 41393 sleep 10 41392 tee myLog41390.log
控制台输出:
BEGIN 52665 this is the parent process in myChild.ksh 52680 go1 this is 52680 child process 20192 zsched 21104 /usr/lib/ssh/sshd 27882 /usr/lib/ssh/sshd -R 28417 /usr/lib/ssh/sshd -R 28531 -ksh 52665 /bin/bash myParent.ksh 52680 /bin/bash $FOLDER/DBB/myChild.ksh 52688 /bin/bash $FOLDER/DBB/myChild.ksh 61896 ptree 52680 52692 tee myLog52680.log 52680 go2 END 52665 this is the parent process
为什么有2个$FOLDER/DBB/myChild.ksh
?
答案1
shell 脚本的后缀是.ksh
,但进程树表明它们实际上是由bash
shell 执行的。
在我的 Debian 11 man ksh
(实际上是mksh
MirBSD Korn shell)上说:
管道的所有命令都在单独的子 shell 中执行;这是 POSIX 允许的,但与 AT&T UNIX ksh 的两个变体不同,其中除了最后一个命令之外的所有命令都在子 shell 中执行;请参阅 read 内置函数的描述以了解含义和解决方法。
并man bash
说:
管道中的每个命令都作为单独的进程(即在子shell 中)执行。 [...] 如果
lastpipe
使用内置函数启用该选项shopt
(请参阅下面的描述shopt
),则管道的最后一个元素可能由 shell 进程运行。
因此,最初进程#41390 是开始执行的进程myChild.ksh
。当它看到管道时:
{
echo " $$ go1 "
toto
ptree $$
echo " $$ go2 "
} | tee myLog$$.log
它将扩展$$
管道中的 ,然后为{
...中的复合命令派生一个子 shell 进程 #41391 }
。
进程 #41392 可以是一个子 shell,它只需要运行一个非内部命令,并将exec()
其作为优化,也可以是由父进程 #41390tee
直接fork()
+ 'd 的命令。exec()
在这两种情况下,它都相当于同一件事:#41392 最终会exec()
执行某些操作,并exec()
导致可见的命令行ptree
发生变化。
另一方面,子 shell #41391 有多个命令要运行,因此进程 #41391 将保持其作为子 shell 的身份。由于它需要执行的最后一个命令是echo
shell 内部命令,因此它将结束其作为子 shell 进程的生命。并且由于其父级 #41390$$
在准备执行管道时已经扩展了 s,因此子 shell #41391 的 PID 将永远不会在输出中可见。