Tcl Expect脚本 - 来自分叉子进程的生成进程永远不会返回EOF

时间:2013-09-06 14:41:54

标签: tcl fork expect spawn

我遇到了另一种奇怪的行为,我似乎无法找到答案。这些真的令人困惑,因为我用最简单的代码看到了这个问题,这几乎完全出自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

使用分叉运行“cat”:

:> 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命令实际上是从forked子进程运行的。我将命令从cat改为类似“touch”的东西,它创建了一个文件,作为它实际产生的证据。但是,产生的进程将其输出定向到其他地方,或者期望没有正确地看到它。我的感觉是它是前者,并且当spawn成功创建一个进程时,它的stdin / stdout可能指向/ dev / null。我没有足够的经验进一步调试。我知道这足以让自己陷入困境,但显然还不足以让自己摆脱困境。

我还希望在3个时间点捕获进程树的快照。父母和孩子都活着的时候。父母去世的另一个,只有孩子还活着。最后,当子进程及其生成的进程存活时。为此,我改为生成类似GUI的东西,直到你死了。我认为“愿望”对此非常完美。在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 / 37,而子进程的TTY为pty / 35(与启动它的tcsh TTY相匹配) 。这与孩子仍然能够向stdout发送文本一致,而spawened进程似乎无法在任何地方发送任何内容。对于Expect产生的过程,是否需要独特的pts / 37 TTY?我以为我记得读过Expect创建了一个新的伪终端,用于与它产生的进程进行通信。但如果是这样的话,为什么Expect会失去与pty / 37连接的产生过程的沟通?

我试图收集更多线索和信息,但这里看起来仍然很可疑。这几乎就是Expect的目的,而且这种行为似乎与我所理解的不符。如果您在设置上运行我的简单示例脚本,其他人是否会看到相同的行为?

提前感谢您的任何帮助或建议。

1 个答案:

答案 0 :(得分:0)

使用:        expect_user [expect_args]              就像期望但它从stdin读取字符(即              来自用户的击键)。默认情况下,执行读取              煮熟的模式。因此,行必须以返回结束              期待看到他们。这可以通过stty改变(参见stty              下面的命令)。

而不是期望为我工作。