我正在尝试使用Perl5来fork()
子进程。子进程应exec()
另一个程序,将其STDIN
重定向到命名管道,将STDOUT
和STDERR
重定向到日志文件。父进程继续在循环中运行,使用waitpid
并检查$?
以重新启动子进程,以防子进程以非零退出状态死亡。
exec()
函数的Perl文档说:
如果LIST中有多个参数,则使用LIST中的参数调用 execvp(3)。如果LIST中只有一个元素,则检查参数是否为shell元字符,如果有,则将整个参数传递给系统的命令shell进行解析(在Unix平台上为
/bin/sh -c
,但在其他平台上有所不同)。如果参数中没有shell元字符,则将其拆分为单词并直接传递给execvp
,这样效率更高。例子:exec '/bin/echo', 'Your arguments are: ', @ARGV; exec "sort $outfile | uniq";
这听起来很酷,我想在没有中间shell的情况下运行我的外部程序,如这些示例所示。不幸的是,我无法将其与输出重定向结合起来(如/bin/foo > /tmp/stdout
)。
换句话说,这不起作用:
exec ( '/bin/ls', '/etc', '>/tmp/stdout' );
所以,我的问题是:如何在不使用shell的情况下重定向我的子命令的STD*
文件?
答案 0 :(得分:5)
通过<
和>
进行重定向是 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');
...或者,使用perldoc
推荐的间接对象语法:
close STDOUT;
open (STDOUT, '>', '/tmp/stdout');
exec { '/bin/ls' } ('ls', '/etc');
(事实上,根据文档,这个最终语法是避免在Windows中实例化shell的唯一可靠方法。)
答案 1 :(得分:4)
以下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
。
但是,不仅仅是解决这个问题(正如格雷格肯尼迪所做的那样),而是将其他问题保持不变(例如,错误地报告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);
}
),我就是这样做的。将告诉你解决所有问题:
use IPC::Run3 qw( run3 );
run3([ '/bin/ls', '/etc' ], \undef, \my $stdout);
虽然这为您节省了一百行代码,open3
仍然是一个非常低级别的代码。 (如果你不得不处理两个管道,你会遇到问题。)我推荐IPC::Run3(更简单)或IPC::Run(更灵活)。
use IPC::Run qw( run );
run([ '/bin/ls', '/etc' ], \undef, \my $stdout);
或
protected function configureListFields(ListMapper $listMapper)
{
$result = $this->getConfigurationPool()->getContainer()->get('Doctrine')->getRepository('UserBundle:User')->findClient();
$listMapper
->add('client', 'sonata_type_model', array(
'empty_value' => '',
'choice_list' => $result))
;
}