我正在尝试使用Perl5来fork()子进程。子进程应该exec()另一个程序,将其STDIN重定向到指定的管道,并将STDOUT和STDERR重定向到记录文件。父进程继续在循环中运行,使用waitpid并检查$?以重新启动子进程,以防其子进程在非零退出状态下死亡。
exec()函数的Perl文档说:
如果LIST中有多个参数,则使用LIST中的参数调用execvp(3)。如果列表中只有一个元素,则会检查参数是否为shell元字符,如果存在,则将整个参数传递给系统的命令shell进行解析(这是Unix平台上的
/bin/sh -c,但在其他平台上有所不同)。如果参数中没有shell元字符,则将其拆分为单词并直接传递给execvp,这样效率更高。示例: 你的论点是:',@ARGV;exec“排序$outfile = uniq";
这听起来很酷,我想在没有中介shell的情况下运行我的外部程序,如下面的例子所示。不幸的是,我无法将它与输出重定向结合起来(如在/bin/foo > /tmp/stdout中)。
换句话说,这不起作用::
exec ( '/bin/ls', '/etc', '>/tmp/stdout' );因此,我的问题是:如何在不使用shell的情况下重定向子命令的STD*文件?
发布于 2017-07-27 16:23:26
通过<和>重定向是shell特性,这就是为什么它在这种用法中不能工作的原因。本质上是调用/bin/ls,并将>/tmp/stdout作为另一个参数传递,在用echo替换命令时很容易看到
exec ('/bin/echo', '/etc', '>/tmp/stdout');指纹:
/etc >/tmp/stdout通常,您的shell (/bin/sh)会解析命令,发现重定向尝试,打开正确的文件,并将参数列表修剪到/bin/echo中。
但是,使用exec() (或system())启动的程序将继承其调用进程的STDIN、STDOUT和STDERR文件。所以,正确的处理方法是
exec()启动程序。重写上面的示例代码,效果很好:
close STDOUT;
open (STDOUT, '>', '/tmp/stdout');
exec ('/bin/ls', '/etc');...or,使用perldoc推荐的间接对象语法。
close STDOUT;
open (STDOUT, '>', '/tmp/stdout');
exec { '/bin/ls' } ('ls', '/etc');(实际上,根据文档,这种最后的语法是避免在Windows中实例化shell的唯一可靠方法。)
发布于 2017-07-27 16:44:30
下面的shell命令告诉shell启动带有/bin/ls的/etc,以便使用其STDOUT重定向的参数。
/bin/ls /etc >/tmp/stdout另一方面,下面的Perl语句告诉Perl用/bin/ls替换当前程序,用/etc和>/tmp/stdout替换参数。
exec('/bin/ls', '/etc', '>/tmp/stdout');您根本没有告诉Perl重定向STDOUT!请记住,exec不会启动新进程,因此如果您更改子进程的fd 1,它将影响在同一进程中运行的ls。
但是,不只是修复这个问题(如Greg ),而是保留您的其他问题(例如,错误地报告无法从ls中启动ls),我将向您展示修复它们的所有方法:
use IPC::Open3 qw( open3 );
my $stdout = '';
{
# open3 will close the handle used as the child's STDIN.
# open3 has issues with lexical file handles.
open(local *CHILD_STDIN, '<', '/dev/null') or die $!;
my $pid = open3('<&CHILD_STDIN', local *CHILD_STDOUT, '>&STDERR',
'/bin/ls', '/etc');
while (my $line = <CHILD_STDOUT>) {
$stdout .= $line;
}
waitpid($pid, 0);
}虽然这为您节省了100行代码,但open3仍然是一个相当低的级别。(如果你必须处理两条管道,你就会遇到麻烦。)相反,我推荐IPC:运行3 (更简单)或IPC::Run (更灵活)。
use IPC::Run3 qw( run3 );
run3([ '/bin/ls', '/etc' ], \undef, \my $stdout);或
use IPC::Run qw( run );
run([ '/bin/ls', '/etc' ], \undef, \my $stdout);https://stackoverflow.com/questions/45356212
复制相似问题