如何从从csh脚本调用的perl脚本中恢复STDIN?

时间:2010-09-28 02:53:22

标签: perl scripting nested stdin csh

我有一个糟糕的嵌套脚本结构,我正在维护,定期从键盘输入用户输入,我正在尝试编写一个自动输入此脚本的脚本。本质上,脚本结构如下所示:

  • cmd1.csh需要3行输入和 然后调用cmd2.csh,然后退出 通常
  • cmd2.csh两次调用cmd3.pl 然后输入1行输入 通常退出
  • cmd3.pl需要1行 通常输入和退出。

不幸的是,我不能永久地更改这些脚本中的任何一个,因此我可以做的任何事情都必须作为整个脚本链的包装器来完成。

我尝试了以下perl脚本的几种变体,但也许我只是没有达到引号和转义的神奇组合:

$cmd = "echo 'a\\
b\\
c\\
d\\
e\\
f' | ~/pltest/cmd1.csh";

system_tcsh

sub system_tcsh
{
   @args = ("tcsh", "-c", shift);
   return system(@args);
}

问题似乎是一旦调用cmd3.pl并读取d,第二次调用cmd3.pl或cmd2.csh就不会读取更多输入。在cmd3.pl中放置多个读取工作正常,从STDIN读取更多项目,但是一旦第一次调用cmd3.pl完成,STDIN就为空(或者处于某种不可用状态)。

当perl脚本返回时STDIN会发生什么,有没有办法使用perl或其他东西来模拟这种输入?

提前致谢。

5 个答案:

答案 0 :(得分:3)

我创造了重现问题的条件,但一切顺利:

caller.pl:

#! /usr/bin/perl

$cmd = "echo 'a\\
b\\
c\\
d\\
e\\
f' | ~/doc/Answers/src/pltest/cmd1.csh";

sub system_tcsh
{
   @args = ("tcsh", "-c", $cmd);
   return system(@args);
}

system_tcsh

cmd1.csh:

#! /bin/csh
echo "${0}(cmd1) $argv[*]"
set line1 = $<
set line2 = $<
set line3 = $<
setenv lineno  3
echo "cmd1: read: $line1 $line2 $line3"
./cmd2.csh

cmd2.csh:

#! /bin/csh
echo "    ${0}(cmd2) $argv[*] now running..."
./cmd3.csh
./cmd3.csh

set line6 = $<

echo "    ${0}: Read: $line6"

cmd3.csh:

#! /bin/csh
# cmd3
set line = $<
echo "        ${0}: Read: '$line'"

试运行:

frayser@gentoo ~/doc/Answers/src/pltest $ ./caller.pl 
/export/home/frayser/doc/Answers/src/pltest/cmd1.csh(cmd1) 
cmd1: read: a b c
    ./cmd2.csh(cmd2)  now running...
        ./cmd3.csh: Read: 'd'
        ./cmd3.csh: Read: 'e'
    ./cmd2.csh: Read: f

也许你可以修改它来重新创建麻烦,或者用它来实现你的解决方案。

-

<强>更新
这是一个更新,它可以重现Perl STDIN问题,并为它提供解决方法。

用Perl版本替换cmd3会产生报告的有问题的结果:

cmd3.pl替换cmd3.csh:

#! /usr/bin/perl
# cmd3

$_=<STDIN>;
chomp();
print "        $0: Read: '$_'\n";

结果:使用 cmd3.pl 。读取“d”后,不再有输入。

./caller.pl    
./cmd1.csh(cmd1) 
cmd1: read: a b c
    ./cmd2.csh(cmd2)  now running...
        ./cmd3.pl: Read: 'd'
        ./cmd3.pl: Read: ''
    ./cmd2.csh: Read: 

为了解决这种情况, cmd2 更改为仅向 cmd3 发送1行。 line(1)命令可以轻松地执行此操作:

#! /bin/csh
echo "    ${0}(cmd2) $argv[*] now running..."
line | ./cmd3.pl
line | ./cmd3.pl

set line6 = $<

echo "    ${0}: Read: $line6"

这是 cmd2.csh 已优化,以避免执行命令的开销。

#! /bin/csh
echo "    ${0}(cmd2) $argv[*] now running..."
set x = $<
echo $x | ./cmd3.pl

set y = $<
echo $y | ./cmd3.pl

set line6 = $<

echo "    ${0}: Read: $line6"

以下是更新后的 cmd2.csh 的输出。使用 Perl 的功能与使用 csh 作为最终脚本时的功能相同:不会丢失标准输入。

./cmd1.csh(cmd1) 
cmd1: read: a b c
    ./cmd2.csh(cmd2)  now running...
        ./cmd3.pl: Read: 'd'
        ./cmd3.pl: Read: 'e'
    ./cmd2.csh: Read: f

答案 1 :(得分:1)

首先,我会使用Expect来编写输入脚本,它可能更可靠,并且在大多数Unix系统上都可用。

除非根据你的echo版本,许多允许在引用的字符串中使用“\ n”,尽管它应该在功能上是等效的。类似的东西:

$cmd = '/bin/echo -e "a\nb\nc\nd\ne\nf" | ~/pltest/cmd1.csh'

(这适用于我的Linux机器.. man echo,以查看您的本地版本的功能。)

但是,如果没有真正了解Perl脚本如何读取输入,很难准确猜出输出的位置,这肯定是一个非常复杂的情况。

答案 2 :(得分:0)

如果你这样做会发生什么:

echo 'a\
b\
c\
d\
e\
f' > tmpfile
cat tmpfile | ~/pltest/cmd1.csh

如果确实有效,您可以围绕预取输入组织Perl脚本,将其保存到临时文件,然后执行上述cat /path/to/tempfile | /path/to/cmd1.csh技巧。

答案 3 :(得分:0)

@just jon:不幸的是,期待我无法使用,而我正在尝试使用我拥有的工具找到一种方法(因为我们的请求过程并不完全合适)。谢谢你的回复! perl脚本只是执行a或者&lt;&gt;,两者似乎都没有正常工作。

@ user268396:这似乎也不起作用,我认为它是功能等价物,因为无论数据来自何处,管道都会强制输入到STDIN。

@Frayser:您是否尝试过将第三个脚本(cmd3.csh)作为perl脚本?这就是我的问题所涉及的。

答案 4 :(得分:0)

This answer about greedy perl stdin from Greg Bacon似乎是解决此问题的方法。当然,这意味着我必须更改最里面的perl脚本,但到目前为止,如果我可以获得更改它的权限,它是最好的解决方案。它不是最强大的解决方案 - 可能使用Expect pm是最好的,但除此之外,这应该有效。