我有一个糟糕的嵌套脚本结构,我正在维护,定期从键盘输入用户输入,我正在尝试编写一个自动输入此脚本的脚本。本质上,脚本结构如下所示:
不幸的是,我不能永久地更改这些脚本中的任何一个,因此我可以做的任何事情都必须作为整个脚本链的包装器来完成。
我尝试了以下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或其他东西来模拟这种输入?
提前致谢。
答案 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是最好的,但除此之外,这应该有效。