首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >重定向STDOUT和STDERR在exec()。无壳

重定向STDOUT和STDERR在exec()。无壳
EN

Stack Overflow用户
提问于 2017-07-27 16:23:26
回答 2查看 2.1K关注 0票数 1

我正在尝试使用Perl5来fork()子进程。子进程应该exec()另一个程序,将其STDIN重定向到指定的管道,并将STDOUTSTDERR重定向到记录文件。父进程继续在循环中运行,使用waitpid并检查$?以重新启动子进程,以防其子进程在非零退出状态下死亡。

exec()函数的Perl文档说:

如果LIST中有多个参数,则使用LIST中的参数调用execvp(3)。如果列表中只有一个元素,则会检查参数是否为shell元字符,如果存在,则将整个参数传递给系统的命令shell进行解析(这是Unix平台上的/bin/sh -c,但在其他平台上有所不同)。如果参数中没有shell元字符,则将其拆分为单词并直接传递给execvp,这样效率更高。示例: 你的论点是:',@ARGV;exec“排序$outfile = uniq";

这听起来很酷,我想在没有中介shell的情况下运行我的外部程序,如下面的例子所示。不幸的是,我无法将它与输出重定向结合起来(如在/bin/foo > /tmp/stdout中)。

换句话说,这不起作用:

代码语言:javascript
复制
exec ( '/bin/ls', '/etc', '>/tmp/stdout' );

因此,我的问题是:如何在不使用shell的情况下重定向子命令的STD*文件?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2017-07-27 16:23:26

通过<>重定向是shell特性,这就是为什么它在这种用法中不能工作的原因。本质上是调用/bin/ls,并将>/tmp/stdout作为另一个参数传递,在用echo替换命令时很容易看到

代码语言:javascript
复制
exec ('/bin/echo', '/etc', '>/tmp/stdout');

指纹:

代码语言:javascript
复制
/etc >/tmp/stdout

通常,您的shell (/bin/sh)会解析命令,发现重定向尝试,打开正确的文件,并将参数列表修剪到/bin/echo中。

但是,使用exec() (或system())启动的程序将继承其调用进程的STDINSTDOUTSTDERR文件。所以,正确的处理方法是

  • 关闭每个特殊的文件句柄,
  • 重新打开它们,指向所需的日志文件,并且
  • 最后调用exec()启动程序。

重写上面的示例代码,效果很好:

代码语言:javascript
复制
close STDOUT;
open (STDOUT, '>', '/tmp/stdout');
exec ('/bin/ls', '/etc');

...or,使用perldoc推荐的间接对象语法。

代码语言:javascript
复制
close STDOUT;
open (STDOUT, '>', '/tmp/stdout');
exec { '/bin/ls' } ('ls', '/etc');

(实际上,根据文档,这种最后的语法是避免在Windows中实例化shell的唯一可靠方法。)

票数 5
EN

Stack Overflow用户

发布于 2017-07-27 16:44:30

下面的shell命令告诉shell启动带有/bin/ls/etc,以便使用其STDOUT重定向的参数。

代码语言:javascript
复制
/bin/ls /etc >/tmp/stdout

另一方面,下面的Perl语句告诉Perl用/bin/ls替换当前程序,用/etc>/tmp/stdout替换参数。

代码语言:javascript
复制
exec('/bin/ls', '/etc', '>/tmp/stdout');

您根本没有告诉Perl重定向STDOUT!请记住,exec不会启动新进程,因此如果您更改子进程的fd 1,它将影响在同一进程中运行的ls

但是,不只是修复这个问题(如Greg ),而是保留您的其他问题(例如,错误地报告无法从ls中启动ls),我将向您展示修复它们的所有方法:

代码语言:javascript
复制
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 (更灵活)。

代码语言:javascript
复制
use IPC::Run3 qw( run3 );
run3([ '/bin/ls', '/etc' ], \undef, \my $stdout);

代码语言:javascript
复制
use IPC::Run qw( run );
run([ '/bin/ls', '/etc' ], \undef, \my $stdout);
票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/45356212

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档