我遇到了另一种我似乎无法找到答案的奇怪行为。这些真的很令人困惑,因为我用最简单的代码看到了这个问题,这几乎是直接出自 Exploring Expect 书。我不确定为什么我会看到这些问题,而且似乎没有其他人有任何问题。
再一次,我把它归结为一个超级简单的脚本。如果我不带参数运行它,它不会将进程分叉给孩子并杀死父母。如果我将任何参数传递给它,它将使用 fork 创建一个孩子。当它作为父进程运行时,它按预期正常工作。但是当它在子进程中运行时,“expect”命令似乎没有连接到任何东西,因为它没有从衍生进程接收任何输出。我只是对这里发生的事情知之甚少,无法调试问题。
简单的(更新的)脚本:
package require Expect
puts "Tcl version : [info tclversion]"
puts "Expect version: [exp_version]"
# Any passed in argument will enable forking to child process
# fork_cat : Forks and then spawns a process to simply cat a text to stdout
# fork_wish: Forks and then spawns a process to start up wish which opens the
# Tk window as well out the interactive prompt % from the interpreter.
if {$argc > 0} {
while {1} {
# If forking fails, retry every 10 seconds until it succeeds.
if {[catch fork child_pid] == 0} {
break
}
sleep 10
}
if {[lindex $argv 0] == "fork_wish"} {
# Delay so process tree snapshot can be captured with both parent and child processes
sleep 20
}
# Kills the parent process to return terminal control to shell
if {$child_pid != 0} {
puts "[pid] Parent process exiting..."
exit
}
if {[lindex $argv 0] == "fork_wish"} {
# Delay so process tree snapshot can be captured of only child process after parent exits
sleep 20
}
# Redefine exit procedure for child so it kills the process for sure on exit
# I have no idea why exit doesn't work for a child process, but this seems to ensure it goes away on exit.
exit -onexit {
puts "[pid] Killing PID..."
exec kill [pid]
}
}
sleep 1
# Show stty output in case it is relevant to debugging
puts ""
stty -a
puts ""
# Spawn process to cat a text file
switch -exact [lindex $argv 0] {
"fork_cat" {
set spawned_pid [spawn -noecho cat 123.txt]
}
"fork_wish" {
set spawned_pid [spawn -noecho wish]
}
default {
set spawned_pid [spawn -noecho cat 123.txt]
}
}
while {1} {
expect {
eof {
puts ""
puts "[pid] Process received EOF from spawned process"
break
}
timeout {
puts ""
puts "[pid] Process expect timed out for spawned process"
}
}
}
puts ""
puts "[pid] Process exiting..."
exit
在没有任何分叉的情况下运行“cat”(正常的前台进程):
:> temp_eof
Tcl version : 8.4
Expect version: 5.43.0
speed 38400 baud; line = 0;
-brkint ixoff -imaxbel
123
123
123
123
123
15060 Process recieved EOF from spawned process
15060 Process exiting...
在没有任何分叉的情况下运行“cat”(后台进程):
:> temp_eof &
[1] 15081
:> Tcl version : 8.4
Expect version: 5.43.0
speed 38400 baud; line = 0;
eof = <undef>; susp = <undef>; rprnt = <undef>; werase = <undef>; lnext = <undef>; min = 1; time = 0;
-brkint inlcr ixoff -imaxbel
-icanon -iexten -echo -echok
123
123
123
123
123
15081 Process received EOF from spawned process
15081 Process exiting...
[1] + Suspended (tty output) temp_eof
[lc-bun-019: AB: ~/bin]
:> fg
temp_eof
用分叉运行“猫”:
:> temp_eof fork_cat
Tcl version : 8.4
Expect version: 5.43.0
15121 Parent process exiting...
:>
speed 38400 baud; line = 0;
eof = <undef>; susp = <undef>; rprnt = <undef>; werase = <undef>; lnext = <undef>; min = 1; time = 0;
-brkint inlcr ixoff -imaxbel
-icanon -iexten -echo -echok
15123 Process expect timed out for spawned process
15123 Process expect timed out for spawned process
15123 Process expect timed out for spawned process
15123 Process expect timed out for spawned process
15123 Process expect timed out for spawned process
15123 Process expect timed out for spawned process
15123 Process expect timed out for spawned process
:> pkill temp_eof
所以问题是,为什么期望从正常的前台进程中正常工作,但在从分叉的子进程执行时似乎没有从衍生进程接收任何输出?也不确定为什么非分叉的后台版本在退出时会暂停 tty。但是您现在可以看到,与从分叉子进程启动的衍生进程相比,正常启动的后台进程中 stty 的输出的相似性。
我已经验证 spawn 命令实际上是从分叉的子进程中运行的。我将命令从 cat 更改为类似于“touch”的命令,它创建一个文件作为它实际生成的证据。但是,生成的进程要么将其输出定向到其他地方,要么期望没有正确看到它。我的感觉是前者,虽然 spawn 成功创建了一个进程,但它的 stdin/stdout 可能指向 /dev/null。我只是没有足够的经验来进一步调试。我知道足以让自己陷入困境,但显然不足以让自己摆脱困境。
我还想在 3 个时间点捕获进程树的快照。当父母和孩子都还活着的时候。另一个是父母去世,只有孩子还活着。最后,当子进程及其衍生进程处于活动状态时。为此,我改为生成类似 GUI 的东西,它会一直保持打开状态,直到你 X 它死了。我认为“希望”非常适合这个。在 20 秒延迟期间,我在一个单独的 shell 中运行“ps axjf”并重定向到一个文件中。该输出的重要部分显示在下面捕获的标准输出下方。
首先是“fork_wish”运行的 shell 输出:
:> temp_eof fork_wish
Tcl version : 8.4
Expect version: 5.43.0
29172 Parent process exiting...
:>
speed 38400 baud; line = 0;
eof = <undef>; susp = <undef>; rprnt = <undef>; werase = <undef>; lnext = <undef>; min = 1; time = 0;
-brkint inlcr ixoff -imaxbel
-icanon -iexten -echo -echok
29174 Process expect timed out for spawned process
29174 Process expect timed out for spawned process
29174 Process expect timed out for spawned process
29174 Process expect timed out for spawned process
29174 Process expect timed out for spawned process
29174 Process expect timed out for spawned process
29174 Process expect timed out for spawned process
:> pkill temp_eof
第一个快照(父母和孩子都活着):
PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
1 24566 24564 20131 pts/30 20131 S 9276 0:00 konsole -T Main /dev/null
24566 24569 24569 24569 pts/35 29172 Ss 9276 0:00 \_ -bin/tcsh
24569 29172 29172 24569 pts/35 29172 Sl+ 9276 0:00 | \_ /usr/local/bin/tclsh temp_eof fork_wish
29172 29174 29172 24569 pts/35 29172 S+ 9276 0:00 | \_ /usr/local/bin/tclsh temp_eof fork_wish
24566 26530 26530 26530 pts/36 29175 Ss 9276 0:00 \_ -bin/tcsh
26530 29175 29175 26530 pts/36 29175 R+ 9276 0:00 \_ ps axjf
第二个快照(孩子活着,父母死了):
PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
1 24566 24564 20131 pts/30 20131 S 9276 0:00 konsole -T Main /dev/null
24566 24569 24569 24569 pts/35 24569 Ss+ 9276 0:00 \_ -bin/tcsh
24566 26530 26530 26530 pts/36 29176 Ss 9276 0:00 \_ -bin/tcsh
26530 29176 29176 26530 pts/36 29176 R+ 9276 0:00 \_ ps axjf
1 29174 29172 24569 pts/35 24569 S 9276 0:00 /usr/local/bin/tclsh temp_eof fork_wish
第三个快照(孩子和产卵活着):
PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
1 24566 24564 20131 pts/30 20131 S 9276 0:00 konsole -T Main /dev/null
24566 24569 24569 24569 pts/35 24569 Ss+ 9276 0:00 \_ -bin/tcsh
24566 26530 26530 26530 pts/36 29192 Ss 9276 0:00 \_ -bin/tcsh
26530 29192 29192 26530 pts/36 29192 R+ 9276 0:00 \_ ps axjf
1 29174 29172 24569 pts/35 24569 S 9276 0:00 /usr/local/bin/tclsh temp_eof fork_wish
29174 29178 29178 29178 pts/37 29178 Ssl+ 9276 0:00 \_ /usr/bin/wish
在这里我觉得奇怪的是,生成的进程有一个新的、唯一的 TTY 为 pty/37,而子进程的 TTY 为 pty/35(与启动它的 tcsh TTY 匹配)。这与孩子仍然能够将文本发送到标准输出一致,而生成的进程似乎无法在任何地方发送任何内容。从 Expect 产生的进程是否需要唯一的 pts/37 TTY?我想我记得读到过,Expect 创建了一个新的伪终端来与其衍生的进程进行通信。但如果是这样的话,为什么 Expect 会失去与连接到 pty/37 的衍生进程的通信?
我试图收集更多的线索和信息,但这里的东西似乎仍然很可疑。这几乎就是 Expect 旨在做的事情,而且这种行为似乎与我所理解的不符。如果您在设置中运行我的简单示例脚本,其他人会看到同样的行为吗?
提前感谢您的任何帮助或建议。