在bash中使用IPC(使用命名管道,而不是expect)

4
我正在编写一个bash脚本,与现有的perl程序进行交互。不幸的是,我不能修改现有的perl程序,也不能使用expect
目前,脚本的工作方式类似于这个stackoverflow答案Is it possible to make a bash shell script interact with another command line program?
问题是(读作:似乎是),perl程序在请求输入之前并不总是发送<newline>。这意味着bash在命名管道上的while ... read无法“获取”(读作:显示)perl程序的输出,因为它一直在等待更多内容。至少我是这样理解的。
所以基本上perl程序正在等待输入,但用户不知道,因为屏幕上没有任何内容。
所以我在bash脚本中做的是:
#!/bin/bash

mkfifo $readpipe
mkfifo $writepipe

[call perl program] < $writepipe &> $readpipe &
exec {FDW}>$writepipe
exec {FDR}<$readpipe

...

while IFS= read -r L
do
    echo "$L"
done < $readpipe

那很好,除非perl程序正在执行类似以下操作的内容
print "\n";
print "Choose action:\n";
print "[A]: Action A      [B]: Action B\n";
print " [C]: cancel\n";
print "    ? ";
print "[C] ";
local $SIG{INT}  = 'IGNORE';
$userin = <STDIN> || ''; chomp $userin;
print "\n";

然后bash脚本只能“看到”
Choose action:
[A]: Action A      [B]: Action B
 [C]: cancel

但不包括
    ? [C]

这并不是最棘手的情况,但它是最容易描述的一个。
有没有办法确保 ? [C] 也被打印出来(我尝试过使用 cat <$readpipe &,但效果不佳)?
或者有更好的方法吗(鉴于限制条件,我不能修改perl程序,也不能使用 expect)?

尝试在[C]后面加上一个换行符,即打印"[C] \n"; - 123
可能可以使用您的coproc方法完成,但由于我手头没有答案,我可以想到一个特别丑陋的解决方法,即在缺失的行后插入换行符(例如:<$writepipe [call perl program] | sed -u 's/ ? \[C\] /&\n/g' &> $readpipe &)。 - bufh
你会使用Python吗?如果可以的话,你可以考虑用一个Python包装器来替换[call perl program],从而在其周围提供伪终端。 /usr/bin/env python -c "import pty, sys; pty.spawn(sys.argv[1:])" /path/to/program.pl < $writepipe &> $readpipe - bishop
1
你考虑过 EmPTY 吗?请查看 http://empty.sourceforge.net/。 - Charles Duffy
在我看来,这似乎是你的 Perl 脚本中的缓冲问题。尝试在 Perl 脚本顶部放置 $|++。我知道这很老旧,但如果这是解决你所看到的问题的方法,那将会很有趣。 - Jim
1个回答

3

请使用read -N1命令。

让我们通过以下示例尝试:与发送提示(不以换行符结束)的程序交互,我们的系统必须发送一些命令,接收发送的命令的回显。也就是说,子进程的总输出为:

$ cat example 
prompt> command1
prompt> command2

这个脚本可能是:

#!/bin/bash 
#

cat example | while IFS=$'\0' read -N1 c; do
  case "$c" in
  ">") 
       echo "received prompt: $buf" 
        # here, sent some command
       buf=""
       ;;
  *)
      if [ "$c" == $'\n' ]; then
        echo "received command: $buf"
        # here, process the command echo
        buf=""
      else
        buf="$buf$c"
      fi
      ;;
  esac
done

产生以下输出的代码如下:
received prompt: prompt
received command:  command1
received prompt: prompt
received command:  command2

这个第二个例子更接近原始问题:

$ cat example

Choose action:
[A]: Action A      [B]: Action B
[C]: cancel
    ? [C]

脚本现在是:

#!/bin/bash 
#

while IFS=$'\0' read -N1 c; do
  case "$c" in
  '?') 
       echo "*** received prompt after: $buf$c ***" 
       echo '*** send C as option ***'
       buf=""
       ;;
  *)
      buf="$buf$c"
      ;;
  esac
done < example

echo "*** final buffer is: $buf ***"

结果如下:

*** received prompt after: 
Choose action:[A]: Action A      [B]: Action B
[C]: cancel
    ? ***
*** send C as option ***
*** final buffer is:  [C]
 ***

我理解并喜欢你的基本想法。不幸的是,在我的情况下它没有起作用。我还没有弄清楚为什么。似乎我的脚本由于某种原因从未读取“?”(或从未看到“\0”?)。 - scherand
@scherand:您能否编辑原始问题,添加新的脚本版本及其输出?在while循环中添加echo“:$c:”作为第一条语句也可能很有趣,以查看处理哪些字符。 - pasaba por aqui

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接