Error[8]: Undefined offset: 33, File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 121
File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 473, decode(

概述如果命令执行失败,我在反引号调用中发现STDERR重定向可能会丢失.我对我所看到的行为感到困惑.$perl -e 'use strict; use warnings; my $out=`DNE`; print $out' Can't exec 'DNE': No such file or directory at -e line 1. Use of un

如果命令执行失败,我在反引号调用中发现STDERR重定向可能会丢失.我对我所看到的行为感到困惑.

$perl -e 'use strict; use warnings; my $out=`DNE`; print $out'  Can't exec "DNE": No such file or directory at -e line 1.Use of uninitialized value in print at -e line 1.$perl -e 'use strict; use warnings; my $out=`DNE 2>&1`; print $out'Use of uninitialized value in print at -e line 1.$perl -e 'use strict; use warnings; my $out=`echo 123; DNE 2>&1`; print $out'123sh: DNE: command not found

我的语法不正确吗?

我在linux上使用Perl 5.8.5.

最佳答案您的语法是正确的,但在一种情况下perl正在删除错误消息.

通常,请考虑在初始化期间测试您的系统是否具有您想要的命令,如果缺少则请尽早失败.

my $foopath = "/usr/bin/foo";dIE "
perl -e 'print "hi\n"; warn "bye\n"'
: $foopath is not executable" unless -x $foopath;# later ...my $output = `$foopath 2>&1`;dIE "
hibye
: $foopath exited $?" if $?;

要完全理解输出的差异,有必要了解Unix系统编程的细节.继续阅读.

Unix文件描述符

考虑一个简单的perl调用.

$perl -e 'print "hi\n"; warn "bye\n"' >/dev/nullbye

它的输出是

$perl -e 'print "hi\n"; warn "bye\n"' 1>/dev/nullbye

请注意,print的输出转到标准输出STDOUT,并警告写入标准错误STDERR.从终端运行时,两者都出现在终端上,但我们可以将它们发送到不同的地方.例如

$perl -e 'print "hi\n"; warn "bye\n"' 2>/dev/nullhi

空设备或/ dev / null会丢弃发送给它的任何输出,因此在上面的命令中,“hi”消失.上面的命令是简写

$perl -e 'print "hi\n"; warn "bye\n"' 2>&1hibye

也就是说,1是STDOUT的文件描述符.相反,扔掉“再见”,跑

$perl -e 'print "hi\n"; warn "bye\n"' >/dev/null 2>&1

如您所见,2是STDERR的文件描述符. (为完整起见,STDIN的文件描述符为0.)

在Bourne shell及其衍生产品中,我们还可以将STDOUT和STDERR合并为2>& 1.将其读作“make file descriptor 2的输出与文件描述符1的相同位置”.

$perl -e 'print "hi\n"; warn "bye\n"' 2>&1 >/dev/nullbye

终端输出不突出显示区别,但额外的重定向显示正在发生的事情.我们可以通过跑步来丢弃它们

fcntl

订单与这个shell系列有关,它以从左到右的顺序处理重定向,因此转换两个产量

$perl -e 'print "hi\n"; warn "bye\n"' | sed -e 's/^/got: /'byegot: hi

起初这可能会令人惊讶. shell首先处理2>& 1,这意味着将STDERR发送到与STDOUT相同的目的地 – 它已经是:终端!然后它处理> / dev / null并将STDOUT重定向到空设备.

这种文件描述符的重复是通过调用@L_301_0@来实现的,这通常是

$perl -e 'print "hi\n"; warn "bye\n"' 2>&1 | sed -e 's/^/got: /'got: byegot: hi
的包装器.

重定向和管道

现在说我们要为命令输出的每一行添加一个前缀.

#! /usr/bin/env perluse strict;use warnings;my $pID = open my $fh,"-|";dIE "open: fork: $!" unless defined $pID;if ($pID) { while (<$fh>) { s/^/got: /; print; }}else { open STDERR,">&=",\*STDOUT or print ""|-": dup: $!"; exec "perl","-e",q[print "hi\n"; warn "bye\n"] or dIE ""-|": exec: $!";}

顺序不同,但请记住STDERR和STDOUT是不同的流.另请注意,只有“hi”才能获得前缀.要获得两条线,它们都必须出现在STDOUT上.

"-"

要构造管道,shell使用fork创建子进程,使用dup2执行重定向,并通过在适当的进程中调用exec来启动管道的每个阶段.对于上面的管道,过程类似于

> shell:fork一个进程来运行sed
> shell:使用waitpID等待sed的退出状态

> sed:创建一个管道以将输入提供给perl
> sed:fork一个进程运行perl
> sed:dup2使STDIN从管道的读取端读取
> sed:执行sed命令
> sed:等待STDIN的输入

> perl:dup2将STDOUT从步骤3发送到管道的写端
> perl:dup2将STDERR发送到STDOUT的目的地
> perl:exec perl命令
> perl:写输出并最终退出

> sed:接收和编辑输入流
> sed:检测管道上的文件结束
> sed:使用waitpID获取perl的退出状态
> sed:退出

> shell:填充$?使用waitpID的返回值

请注意,子进程是按从右到左的顺序创建的.这是因为Bourne系列中的shell将管道的退出状态定义为最后一个进程的退出状态.

自己动手管道

您可以使用下面的代码在Perl中构建上述管道.

$./simple-pipelinegot: byegot: hi

第一次打开电话为我们做了很多工作,如perlfunc documentation on

$./simple-pipeline >/dev/null
所述:

For three or more arguments if MODE is pp_backtick,the filename is interpreted as a command to which output is to be piped,and if MODE is Perl_my_popen,the filename is interpreted as a command that pipes output to us. In the two-argument (and one-argument) form,one should replace dash (/* handle the 2>&1 construct at the end */) with the command. See 07003 for more examples of this.

它的输出是

if (*s == '>' && s[1] == '&' && s[2] == '1' && s > cmd + 1 && s[-1] == '2' && isspace(s[-2]) && (!s[3] || isspace(s[3]))){ const char *t = s + 3; while (*t && isspace(*t)) ++t; if (!*t && (PerlliO_dup2(1,2) != -1)) { s[-2] = 'PerlProc_execl(PL_sh_path,"sh","-c",cmd,(char *)NulL);PERL_FPU_POST_EXECS_exec_Failed(aTHX_ PL_sh_path,fd,do_report);'; break; }}

上面的代码硬编码了STDOUT的复制,我们可以在下面看到.

if (ckWARN(WARN_EXEC)) Perl_warner(aTHX_ packWARN(WARN_EXEC),"Can't exec \"%s\": %s",Strerror(e));

Perl反引号

为了捕获另一个命令的输出,perl设置了相同的机制,你可以在

$perl -e 'use strict; use warnings; my $out=`DNE`; print $out'Can't exec "DNE": No such file or directory at -e line 1.Use of uninitialized value in print at -e line 1.
(在pp_sys.c中)看到它,它调用if (*s != ' ' && !isAlpha(*s) && strchr("$&*(){}[]'\";\|?<>~`\n",*s)) {(在util.c中)来创建子进程并设置管道(fork,管道,dup2).孩子做了一些管道并调用Perl_do_exec3(在doio.c中)来启动我们想要的输出命令.我们注意到a relevant comment:

$perl -e 'use strict; use warnings; my $out=`DNE 2>&1`; print $out'Use of uninitialized value in print at -e line 1.

该实现识别序列2>& 1,复制STDOUT,并从命令中移除重定向以传递给shell.

if (!*t && (PerlliO_dup2(1,2) != -1)) {    s[-2] = '
$perl -e 'use strict; use warnings; my $out=`echo 123; DNE 2>&1`; print $out'123sh: DNE: command not found
'; break;}

后来我们看到了

$perl -Mstrict -Mwarnings -e 'my $out=`DNE`; print $out'Can't exec "DNE": No such file or directory at -e line 1.Use of uninitialized value $out in print at -e line 1.$perl -Mstrict -Mwarnings -M-warnings=exec -e 'my $out=`DNE`; print $out'Use of uninitialized value $out in print at -e line 1.

在S_exec_Failed内部,我们发现

use strict;use warnings;no warnings 'exec';my $out = `DNE`;print defined($out) ? $out : "command Failed\n";

这是您在问题中提出的警告之一.

时间线

让我们详细了解perl如何处理您问题中的命令.

正如所料

[+++]

这里没有惊喜.

一个微妙的细节是很重要的理解.仅当条件为要执行的命令为真时,上面处理2>& 1内部的代码才会运行:

[+++]

这是一个优化.如果反引号中的命令包含上面的shell元字符,则perl必须将其移交给shell.但是如果没有shell元字符存在,perl可以直接执行命令 – 节省fork和shell启动成本.

不存在的命令DNE不包含shell元字符,因此perl完成所有工作.生成exec-category警告是因为命令失败并且您启用了警告编译指示. perlop文档告诉我们,当命令失败时,反引号或qx //在标量上下文中返回undef,这就是为什么你得到关于打印$out的未定义值的警告.

缺少警告

[+++]

失败的执行警告在哪里?

请记住创建运行另一个命令的子进程的基本步骤:

>为子项创建管道以将其输出发送到父项.
>调用fork创建一个几乎相同的子进程.
>在子项中,dup2将STDOUT连接到管道的写入端.
>在子进程中,exec使新创建的子进程执行另一个程序.
>在父级中,读取管道的内容.

为了捕获另一个命令的输出,perl完成了这些步骤.在准备尝试运行DNE 2>& 1时,perl会分叉一个孩子并且在子进程中导致STDERR成为STDOUT的副本,但是存在另一个副作用.

[+++]

如果2>& 1在命令的末尾并且dup2成功,则perl在重定向之前写入Nul字节.这具有从命令中移除它的效果,例如,DNE 2>& 1变为DNE!现在,在命令中没有shell元字符的情况下,子进程中的perl认为自己是’Self,我们可以直接执行这个命令’.

对exec的调用失败,因为DNE不存在.孩子仍然在STDERR上发出失败的exec警告.由于dup2将STDERR指向与STDOUT相同的位置,因此它不会转到终端:管道的写入端返回到父节点.

父进程检测到子进程异常退出,并忽略管道内容,因为命令执行失败的结果记录为undef.

不同的警告

[+++]

在这里,我们看到不存在DNE的不同诊断.遇到的第一个shell元字符是;,因此perl将命令不变地移交给shell执行.回声正常完成,然后DNE在shell中失败,shell的STDOUT和STDERR返回到父进程.从perl的角度来看,shell执行得很好,所以没有什么值得警告的.

相关说明

当您启用警告pragma-a Very Good Practice! – 这将启用exec警告类别.要查看这些警告的完整列表,请在perldiag文档中搜索字符串W exec.

观察差异.

[+++]

后者的调用相当于

[+++]

我喜欢在执行exec,管道打开等问题时格式化我自己的错误消息.这意味着我通常禁用exec警告,但这也意味着我必须非常小心地测试返回值. 总结

以上是内存溢出为你收集整理的linux – 如果反引号不能执行,则STDER的STDERR重定向会丢失全部内容,希望文章能够帮你解决linux – 如果反引号不能执行,则STDER的STDERR重定向会丢失所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

)
File: /www/wwwroot/outofmemory.cn/tmp/route_read.php, Line: 126, InsideLink()
File: /www/wwwroot/outofmemory.cn/tmp/index.inc.php, Line: 166, include(/www/wwwroot/outofmemory.cn/tmp/route_read.php)
File: /www/wwwroot/outofmemory.cn/index.php, Line: 30, include(/www/wwwroot/outofmemory.cn/tmp/index.inc.php)
Error[8]: Undefined offset: 34, File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 121
File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 473, decode(

概述如果命令执行失败,我在反引号调用中发现STDERR重定向可能会丢失.我对我所看到的行为感到困惑.$perl -e 'use strict; use warnings; my $out=`DNE`; print $out' Can't exec 'DNE': No such file or directory at -e line 1. Use of un

如果命令执行失败,我在反引号调用中发现STDERR重定向可能会丢失.我对我所看到的行为感到困惑.

$perl -e 'use strict; use warnings; my $out=`DNE`; print $out'  Can't exec "DNE": No such file or directory at -e line 1.Use of uninitialized value in print at -e line 1.$perl -e 'use strict; use warnings; my $out=`DNE 2>&1`; print $out'Use of uninitialized value in print at -e line 1.$perl -e 'use strict; use warnings; my $out=`echo 123; DNE 2>&1`; print $out'123sh: DNE: command not found

我的语法不正确吗?

我在linux上使用Perl 5.8.5.

最佳答案您的语法是正确的,但在一种情况下perl正在删除错误消息.

通常,请考虑在初始化期间测试您的系统是否具有您想要的命令,如果缺少则请尽早失败.

my $foopath = "/usr/bin/foo";dIE "
perl -e 'print "hi\n"; warn "bye\n"'
: $foopath is not executable" unless -x $foopath;# later ...my $output = `$foopath 2>&1`;dIE "
hibye
: $foopath exited $?" if $?;

要完全理解输出的差异,有必要了解Unix系统编程的细节.继续阅读.

Unix文件描述符

考虑一个简单的perl调用.

$perl -e 'print "hi\n"; warn "bye\n"' >/dev/nullbye

它的输出是

$perl -e 'print "hi\n"; warn "bye\n"' 1>/dev/nullbye

请注意,print的输出转到标准输出STDOUT,并警告写入标准错误STDERR.从终端运行时,两者都出现在终端上,但我们可以将它们发送到不同的地方.例如

$perl -e 'print "hi\n"; warn "bye\n"' 2>/dev/nullhi

空设备或/ dev / null会丢弃发送给它的任何输出,因此在上面的命令中,“hi”消失.上面的命令是简写

$perl -e 'print "hi\n"; warn "bye\n"' 2>&1hibye

也就是说,1是STDOUT的文件描述符.相反,扔掉“再见”,跑

$perl -e 'print "hi\n"; warn "bye\n"' >/dev/null 2>&1

如您所见,2是STDERR的文件描述符. (为完整起见,STDIN的文件描述符为0.)

在Bourne shell及其衍生产品中,我们还可以将STDOUT和STDERR合并为2>& 1.将其读作“make file descriptor 2的输出与文件描述符1的相同位置”.

$perl -e 'print "hi\n"; warn "bye\n"' 2>&1 >/dev/nullbye

终端输出不突出显示区别,但额外的重定向显示正在发生的事情.我们可以通过跑步来丢弃它们

fcntl

订单与这个shell系列有关,它以从左到右的顺序处理重定向,因此转换两个产量

$perl -e 'print "hi\n"; warn "bye\n"' | sed -e 's/^/got: /'byegot: hi

起初这可能会令人惊讶. shell首先处理2>& 1,这意味着将STDERR发送到与STDOUT相同的目的地 – 它已经是:终端!然后它处理> / dev / null并将STDOUT重定向到空设备.

这种文件描述符的重复是通过调用@L_301_0@来实现的,这通常是

$perl -e 'print "hi\n"; warn "bye\n"' 2>&1 | sed -e 's/^/got: /'got: byegot: hi
的包装器.

重定向和管道

现在说我们要为命令输出的每一行添加一个前缀.

#! /usr/bin/env perluse strict;use warnings;my $pID = open my $fh,"-|";dIE "open: fork: $!" unless defined $pID;if ($pID) { while (<$fh>) { s/^/got: /; print; }}else { open STDERR,">&=",\*STDOUT or print ""|-": dup: $!"; exec "perl","-e",q[print "hi\n"; warn "bye\n"] or dIE ""-|": exec: $!";}

顺序不同,但请记住STDERR和STDOUT是不同的流.另请注意,只有“hi”才能获得前缀.要获得两条线,它们都必须出现在STDOUT上.

"-"

要构造管道,shell使用fork创建子进程,使用dup2执行重定向,并通过在适当的进程中调用exec来启动管道的每个阶段.对于上面的管道,过程类似于

> shell:fork一个进程来运行sed
> shell:使用waitpID等待sed的退出状态

> sed:创建一个管道以将输入提供给perl
> sed:fork一个进程运行perl
> sed:dup2使STDIN从管道的读取端读取
> sed:执行sed命令
> sed:等待STDIN的输入

> perl:dup2将STDOUT从步骤3发送到管道的写端
> perl:dup2将STDERR发送到STDOUT的目的地
> perl:exec perl命令
> perl:写输出并最终退出

> sed:接收和编辑输入流
> sed:检测管道上的文件结束
> sed:使用waitpID获取perl的退出状态
> sed:退出

> shell:填充$?使用waitpID的返回值

请注意,子进程是按从右到左的顺序创建的.这是因为Bourne系列中的shell将管道的退出状态定义为最后一个进程的退出状态.

自己动手管道

您可以使用下面的代码在Perl中构建上述管道.

$./simple-pipelinegot: byegot: hi

第一次打开电话为我们做了很多工作,如perlfunc documentation on

$./simple-pipeline >/dev/null
所述:

For three or more arguments if MODE is pp_backtick,the filename is interpreted as a command to which output is to be piped,and if MODE is Perl_my_popen,the filename is interpreted as a command that pipes output to us. In the two-argument (and one-argument) form,one should replace dash (/* handle the 2>&1 construct at the end */) with the command. See 07003 for more examples of this.

它的输出是

if (*s == '>' && s[1] == '&' && s[2] == '1' && s > cmd + 1 && s[-1] == '2' && isspace(s[-2]) && (!s[3] || isspace(s[3]))){ const char *t = s + 3; while (*t && isspace(*t)) ++t; if (!*t && (PerlliO_dup2(1,2) != -1)) { s[-2] = 'PerlProc_execl(PL_sh_path,"sh","-c",cmd,(char *)NulL);PERL_FPU_POST_EXECS_exec_Failed(aTHX_ PL_sh_path,fd,do_report);'; break; }}

上面的代码硬编码了STDOUT的复制,我们可以在下面看到.

if (ckWARN(WARN_EXEC)) Perl_warner(aTHX_ packWARN(WARN_EXEC),"Can't exec \"%s\": %s",Strerror(e));

Perl反引号

为了捕获另一个命令的输出,perl设置了相同的机制,你可以在

$perl -e 'use strict; use warnings; my $out=`DNE`; print $out'Can't exec "DNE": No such file or directory at -e line 1.Use of uninitialized value in print at -e line 1.
(在pp_sys.c中)看到它,它调用if (*s != ' ' && !isAlpha(*s) && strchr("$&*(){}[]'\";\|?<>~`\n",*s)) {(在util.c中)来创建子进程并设置管道(fork,管道,dup2).孩子做了一些管道并调用Perl_do_exec3(在doio.c中)来启动我们想要的输出命令.我们注意到a relevant comment:

$perl -e 'use strict; use warnings; my $out=`DNE 2>&1`; print $out'Use of uninitialized value in print at -e line 1.

该实现识别序列2>& 1,复制STDOUT,并从命令中移除重定向以传递给shell.

if (!*t && (PerlliO_dup2(1,2) != -1)) {    s[-2] = '
$perl -e 'use strict; use warnings; my $out=`echo 123; DNE 2>&1`; print $out'123sh: DNE: command not found
'; break;}

后来我们看到了

$perl -Mstrict -Mwarnings -e 'my $out=`DNE`; print $out'Can't exec "DNE": No such file or directory at -e line 1.Use of uninitialized value $out in print at -e line 1.$perl -Mstrict -Mwarnings -M-warnings=exec -e 'my $out=`DNE`; print $out'Use of uninitialized value $out in print at -e line 1.

在S_exec_Failed内部,我们发现

use strict;use warnings;no warnings 'exec';my $out = `DNE`;print defined($out) ? $out : "command Failed\n";

这是您在问题中提出的警告之一.

时间线

让我们详细了解perl如何处理您问题中的命令.

正如所料

这里没有惊喜.

一个微妙的细节是很重要的理解.仅当条件为要执行的命令为真时,上面处理2>& 1内部的代码才会运行:

[+++]

这是一个优化.如果反引号中的命令包含上面的shell元字符,则perl必须将其移交给shell.但是如果没有shell元字符存在,perl可以直接执行命令 – 节省fork和shell启动成本.

不存在的命令DNE不包含shell元字符,因此perl完成所有工作.生成exec-category警告是因为命令失败并且您启用了警告编译指示. perlop文档告诉我们,当命令失败时,反引号或qx //在标量上下文中返回undef,这就是为什么你得到关于打印$out的未定义值的警告.

缺少警告

[+++]

失败的执行警告在哪里?

请记住创建运行另一个命令的子进程的基本步骤:

>为子项创建管道以将其输出发送到父项.
>调用fork创建一个几乎相同的子进程.
>在子项中,dup2将STDOUT连接到管道的写入端.
>在子进程中,exec使新创建的子进程执行另一个程序.
>在父级中,读取管道的内容.

为了捕获另一个命令的输出,perl完成了这些步骤.在准备尝试运行DNE 2>& 1时,perl会分叉一个孩子并且在子进程中导致STDERR成为STDOUT的副本,但是存在另一个副作用.

[+++]

如果2>& 1在命令的末尾并且dup2成功,则perl在重定向之前写入Nul字节.这具有从命令中移除它的效果,例如,DNE 2>& 1变为DNE!现在,在命令中没有shell元字符的情况下,子进程中的perl认为自己是’Self,我们可以直接执行这个命令’.

对exec的调用失败,因为DNE不存在.孩子仍然在STDERR上发出失败的exec警告.由于dup2将STDERR指向与STDOUT相同的位置,因此它不会转到终端:管道的写入端返回到父节点.

父进程检测到子进程异常退出,并忽略管道内容,因为命令执行失败的结果记录为undef.

不同的警告

[+++]

在这里,我们看到不存在DNE的不同诊断.遇到的第一个shell元字符是;,因此perl将命令不变地移交给shell执行.回声正常完成,然后DNE在shell中失败,shell的STDOUT和STDERR返回到父进程.从perl的角度来看,shell执行得很好,所以没有什么值得警告的.

相关说明

当您启用警告pragma-a Very Good Practice! – 这将启用exec警告类别.要查看这些警告的完整列表,请在perldiag文档中搜索字符串W exec.

观察差异.

[+++]

后者的调用相当于

[+++]

我喜欢在执行exec,管道打开等问题时格式化我自己的错误消息.这意味着我通常禁用exec警告,但这也意味着我必须非常小心地测试返回值. 总结

以上是内存溢出为你收集整理的linux – 如果反引号不能执行,则STDER的STDERR重定向会丢失全部内容,希望文章能够帮你解决linux – 如果反引号不能执行,则STDER的STDERR重定向会丢失所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

)
File: /www/wwwroot/outofmemory.cn/tmp/route_read.php, Line: 126, InsideLink()
File: /www/wwwroot/outofmemory.cn/tmp/index.inc.php, Line: 166, include(/www/wwwroot/outofmemory.cn/tmp/route_read.php)
File: /www/wwwroot/outofmemory.cn/index.php, Line: 30, include(/www/wwwroot/outofmemory.cn/tmp/index.inc.php)
Error[8]: Undefined offset: 35, File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 121
File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 473, decode(

概述如果命令执行失败,我在反引号调用中发现STDERR重定向可能会丢失.我对我所看到的行为感到困惑.$perl -e 'use strict; use warnings; my $out=`DNE`; print $out' Can't exec 'DNE': No such file or directory at -e line 1. Use of un

如果命令执行失败,我在反引号调用中发现STDERR重定向可能会丢失.我对我所看到的行为感到困惑.

$perl -e 'use strict; use warnings; my $out=`DNE`; print $out'  Can't exec "DNE": No such file or directory at -e line 1.Use of uninitialized value in print at -e line 1.$perl -e 'use strict; use warnings; my $out=`DNE 2>&1`; print $out'Use of uninitialized value in print at -e line 1.$perl -e 'use strict; use warnings; my $out=`echo 123; DNE 2>&1`; print $out'123sh: DNE: command not found

我的语法不正确吗?

我在linux上使用Perl 5.8.5.

最佳答案您的语法是正确的,但在一种情况下perl正在删除错误消息.

通常,请考虑在初始化期间测试您的系统是否具有您想要的命令,如果缺少则请尽早失败.

my $foopath = "/usr/bin/foo";dIE "
perl -e 'print "hi\n"; warn "bye\n"'
: $foopath is not executable" unless -x $foopath;# later ...my $output = `$foopath 2>&1`;dIE "
hibye
: $foopath exited $?" if $?;

要完全理解输出的差异,有必要了解Unix系统编程的细节.继续阅读.

Unix文件描述符

考虑一个简单的perl调用.

$perl -e 'print "hi\n"; warn "bye\n"' >/dev/nullbye

它的输出是

$perl -e 'print "hi\n"; warn "bye\n"' 1>/dev/nullbye

请注意,print的输出转到标准输出STDOUT,并警告写入标准错误STDERR.从终端运行时,两者都出现在终端上,但我们可以将它们发送到不同的地方.例如

$perl -e 'print "hi\n"; warn "bye\n"' 2>/dev/nullhi

空设备或/ dev / null会丢弃发送给它的任何输出,因此在上面的命令中,“hi”消失.上面的命令是简写

$perl -e 'print "hi\n"; warn "bye\n"' 2>&1hibye

也就是说,1是STDOUT的文件描述符.相反,扔掉“再见”,跑

$perl -e 'print "hi\n"; warn "bye\n"' >/dev/null 2>&1

如您所见,2是STDERR的文件描述符. (为完整起见,STDIN的文件描述符为0.)

在Bourne shell及其衍生产品中,我们还可以将STDOUT和STDERR合并为2>& 1.将其读作“make file descriptor 2的输出与文件描述符1的相同位置”.

$perl -e 'print "hi\n"; warn "bye\n"' 2>&1 >/dev/nullbye

终端输出不突出显示区别,但额外的重定向显示正在发生的事情.我们可以通过跑步来丢弃它们

fcntl

订单与这个shell系列有关,它以从左到右的顺序处理重定向,因此转换两个产量

$perl -e 'print "hi\n"; warn "bye\n"' | sed -e 's/^/got: /'byegot: hi

起初这可能会令人惊讶. shell首先处理2>& 1,这意味着将STDERR发送到与STDOUT相同的目的地 – 它已经是:终端!然后它处理> / dev / null并将STDOUT重定向到空设备.

这种文件描述符的重复是通过调用@L_301_0@来实现的,这通常是

$perl -e 'print "hi\n"; warn "bye\n"' 2>&1 | sed -e 's/^/got: /'got: byegot: hi
的包装器.

重定向和管道

现在说我们要为命令输出的每一行添加一个前缀.

#! /usr/bin/env perluse strict;use warnings;my $pID = open my $fh,"-|";dIE "open: fork: $!" unless defined $pID;if ($pID) { while (<$fh>) { s/^/got: /; print; }}else { open STDERR,">&=",\*STDOUT or print ""|-": dup: $!"; exec "perl","-e",q[print "hi\n"; warn "bye\n"] or dIE ""-|": exec: $!";}

顺序不同,但请记住STDERR和STDOUT是不同的流.另请注意,只有“hi”才能获得前缀.要获得两条线,它们都必须出现在STDOUT上.

"-"

要构造管道,shell使用fork创建子进程,使用dup2执行重定向,并通过在适当的进程中调用exec来启动管道的每个阶段.对于上面的管道,过程类似于

> shell:fork一个进程来运行sed
> shell:使用waitpID等待sed的退出状态

> sed:创建一个管道以将输入提供给perl
> sed:fork一个进程运行perl
> sed:dup2使STDIN从管道的读取端读取
> sed:执行sed命令
> sed:等待STDIN的输入

> perl:dup2将STDOUT从步骤3发送到管道的写端
> perl:dup2将STDERR发送到STDOUT的目的地
> perl:exec perl命令
> perl:写输出并最终退出

> sed:接收和编辑输入流
> sed:检测管道上的文件结束
> sed:使用waitpID获取perl的退出状态
> sed:退出

> shell:填充$?使用waitpID的返回值

请注意,子进程是按从右到左的顺序创建的.这是因为Bourne系列中的shell将管道的退出状态定义为最后一个进程的退出状态.

自己动手管道

您可以使用下面的代码在Perl中构建上述管道.

$./simple-pipelinegot: byegot: hi

第一次打开电话为我们做了很多工作,如perlfunc documentation on

$./simple-pipeline >/dev/null
所述:

For three or more arguments if MODE is pp_backtick,the filename is interpreted as a command to which output is to be piped,and if MODE is Perl_my_popen,the filename is interpreted as a command that pipes output to us. In the two-argument (and one-argument) form,one should replace dash (/* handle the 2>&1 construct at the end */) with the command. See 07003 for more examples of this.

它的输出是

if (*s == '>' && s[1] == '&' && s[2] == '1' && s > cmd + 1 && s[-1] == '2' && isspace(s[-2]) && (!s[3] || isspace(s[3]))){ const char *t = s + 3; while (*t && isspace(*t)) ++t; if (!*t && (PerlliO_dup2(1,2) != -1)) { s[-2] = 'PerlProc_execl(PL_sh_path,"sh","-c",cmd,(char *)NulL);PERL_FPU_POST_EXECS_exec_Failed(aTHX_ PL_sh_path,fd,do_report);'; break; }}

上面的代码硬编码了STDOUT的复制,我们可以在下面看到.

if (ckWARN(WARN_EXEC)) Perl_warner(aTHX_ packWARN(WARN_EXEC),"Can't exec \"%s\": %s",Strerror(e));

Perl反引号

为了捕获另一个命令的输出,perl设置了相同的机制,你可以在

$perl -e 'use strict; use warnings; my $out=`DNE`; print $out'Can't exec "DNE": No such file or directory at -e line 1.Use of uninitialized value in print at -e line 1.
(在pp_sys.c中)看到它,它调用if (*s != ' ' && !isAlpha(*s) && strchr("$&*(){}[]'\";\|?<>~`\n",*s)) {(在util.c中)来创建子进程并设置管道(fork,管道,dup2).孩子做了一些管道并调用Perl_do_exec3(在doio.c中)来启动我们想要的输出命令.我们注意到a relevant comment:

$perl -e 'use strict; use warnings; my $out=`DNE 2>&1`; print $out'Use of uninitialized value in print at -e line 1.

该实现识别序列2>& 1,复制STDOUT,并从命令中移除重定向以传递给shell.

if (!*t && (PerlliO_dup2(1,2) != -1)) {    s[-2] = '
$perl -e 'use strict; use warnings; my $out=`echo 123; DNE 2>&1`; print $out'123sh: DNE: command not found
'; break;}

后来我们看到了

$perl -Mstrict -Mwarnings -e 'my $out=`DNE`; print $out'Can't exec "DNE": No such file or directory at -e line 1.Use of uninitialized value $out in print at -e line 1.$perl -Mstrict -Mwarnings -M-warnings=exec -e 'my $out=`DNE`; print $out'Use of uninitialized value $out in print at -e line 1.

在S_exec_Failed内部,我们发现

use strict;use warnings;no warnings 'exec';my $out = `DNE`;print defined($out) ? $out : "command Failed\n";

这是您在问题中提出的警告之一.

时间线

让我们详细了解perl如何处理您问题中的命令.

正如所料

这里没有惊喜.

一个微妙的细节是很重要的理解.仅当条件为要执行的命令为真时,上面处理2>& 1内部的代码才会运行:

这是一个优化.如果反引号中的命令包含上面的shell元字符,则perl必须将其移交给shell.但是如果没有shell元字符存在,perl可以直接执行命令 – 节省fork和shell启动成本.

不存在的命令DNE不包含shell元字符,因此perl完成所有工作.生成exec-category警告是因为命令失败并且您启用了警告编译指示. perlop文档告诉我们,当命令失败时,反引号或qx //在标量上下文中返回undef,这就是为什么你得到关于打印$out的未定义值的警告.

缺少警告

[+++]

失败的执行警告在哪里?

请记住创建运行另一个命令的子进程的基本步骤:

>为子项创建管道以将其输出发送到父项.
>调用fork创建一个几乎相同的子进程.
>在子项中,dup2将STDOUT连接到管道的写入端.
>在子进程中,exec使新创建的子进程执行另一个程序.
>在父级中,读取管道的内容.

为了捕获另一个命令的输出,perl完成了这些步骤.在准备尝试运行DNE 2>& 1时,perl会分叉一个孩子并且在子进程中导致STDERR成为STDOUT的副本,但是存在另一个副作用.

[+++]

如果2>& 1在命令的末尾并且dup2成功,则perl在重定向之前写入Nul字节.这具有从命令中移除它的效果,例如,DNE 2>& 1变为DNE!现在,在命令中没有shell元字符的情况下,子进程中的perl认为自己是’Self,我们可以直接执行这个命令’.

对exec的调用失败,因为DNE不存在.孩子仍然在STDERR上发出失败的exec警告.由于dup2将STDERR指向与STDOUT相同的位置,因此它不会转到终端:管道的写入端返回到父节点.

父进程检测到子进程异常退出,并忽略管道内容,因为命令执行失败的结果记录为undef.

不同的警告

[+++]

在这里,我们看到不存在DNE的不同诊断.遇到的第一个shell元字符是;,因此perl将命令不变地移交给shell执行.回声正常完成,然后DNE在shell中失败,shell的STDOUT和STDERR返回到父进程.从perl的角度来看,shell执行得很好,所以没有什么值得警告的.

相关说明

当您启用警告pragma-a Very Good Practice! – 这将启用exec警告类别.要查看这些警告的完整列表,请在perldiag文档中搜索字符串W exec.

观察差异.

[+++]

后者的调用相当于

[+++]

我喜欢在执行exec,管道打开等问题时格式化我自己的错误消息.这意味着我通常禁用exec警告,但这也意味着我必须非常小心地测试返回值. 总结

以上是内存溢出为你收集整理的linux – 如果反引号不能执行,则STDER的STDERR重定向会丢失全部内容,希望文章能够帮你解决linux – 如果反引号不能执行,则STDER的STDERR重定向会丢失所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

)
File: /www/wwwroot/outofmemory.cn/tmp/route_read.php, Line: 126, InsideLink()
File: /www/wwwroot/outofmemory.cn/tmp/index.inc.php, Line: 166, include(/www/wwwroot/outofmemory.cn/tmp/route_read.php)
File: /www/wwwroot/outofmemory.cn/index.php, Line: 30, include(/www/wwwroot/outofmemory.cn/tmp/index.inc.php)
Error[8]: Undefined offset: 36, File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 121
File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 473, decode(

概述如果命令执行失败,我在反引号调用中发现STDERR重定向可能会丢失.我对我所看到的行为感到困惑.$perl -e 'use strict; use warnings; my $out=`DNE`; print $out' Can't exec 'DNE': No such file or directory at -e line 1. Use of un

如果命令执行失败,我在反引号调用中发现STDERR重定向可能会丢失.我对我所看到的行为感到困惑.

$perl -e 'use strict; use warnings; my $out=`DNE`; print $out'  Can't exec "DNE": No such file or directory at -e line 1.Use of uninitialized value in print at -e line 1.$perl -e 'use strict; use warnings; my $out=`DNE 2>&1`; print $out'Use of uninitialized value in print at -e line 1.$perl -e 'use strict; use warnings; my $out=`echo 123; DNE 2>&1`; print $out'123sh: DNE: command not found

我的语法不正确吗?

我在linux上使用Perl 5.8.5.

最佳答案您的语法是正确的,但在一种情况下perl正在删除错误消息.

通常,请考虑在初始化期间测试您的系统是否具有您想要的命令,如果缺少则请尽早失败.

my $foopath = "/usr/bin/foo";dIE "
perl -e 'print "hi\n"; warn "bye\n"'
: $foopath is not executable" unless -x $foopath;# later ...my $output = `$foopath 2>&1`;dIE "
hibye
: $foopath exited $?" if $?;

要完全理解输出的差异,有必要了解Unix系统编程的细节.继续阅读.

Unix文件描述符

考虑一个简单的perl调用.

$perl -e 'print "hi\n"; warn "bye\n"' >/dev/nullbye

它的输出是

$perl -e 'print "hi\n"; warn "bye\n"' 1>/dev/nullbye

请注意,print的输出转到标准输出STDOUT,并警告写入标准错误STDERR.从终端运行时,两者都出现在终端上,但我们可以将它们发送到不同的地方.例如

$perl -e 'print "hi\n"; warn "bye\n"' 2>/dev/nullhi

空设备或/ dev / null会丢弃发送给它的任何输出,因此在上面的命令中,“hi”消失.上面的命令是简写

$perl -e 'print "hi\n"; warn "bye\n"' 2>&1hibye

也就是说,1是STDOUT的文件描述符.相反,扔掉“再见”,跑

$perl -e 'print "hi\n"; warn "bye\n"' >/dev/null 2>&1

如您所见,2是STDERR的文件描述符. (为完整起见,STDIN的文件描述符为0.)

在Bourne shell及其衍生产品中,我们还可以将STDOUT和STDERR合并为2>& 1.将其读作“make file descriptor 2的输出与文件描述符1的相同位置”.

$perl -e 'print "hi\n"; warn "bye\n"' 2>&1 >/dev/nullbye

终端输出不突出显示区别,但额外的重定向显示正在发生的事情.我们可以通过跑步来丢弃它们

fcntl

订单与这个shell系列有关,它以从左到右的顺序处理重定向,因此转换两个产量

$perl -e 'print "hi\n"; warn "bye\n"' | sed -e 's/^/got: /'byegot: hi

起初这可能会令人惊讶. shell首先处理2>& 1,这意味着将STDERR发送到与STDOUT相同的目的地 – 它已经是:终端!然后它处理> / dev / null并将STDOUT重定向到空设备.

这种文件描述符的重复是通过调用@L_301_0@来实现的,这通常是

$perl -e 'print "hi\n"; warn "bye\n"' 2>&1 | sed -e 's/^/got: /'got: byegot: hi
的包装器.

重定向和管道

现在说我们要为命令输出的每一行添加一个前缀.

#! /usr/bin/env perluse strict;use warnings;my $pID = open my $fh,"-|";dIE "open: fork: $!" unless defined $pID;if ($pID) { while (<$fh>) { s/^/got: /; print; }}else { open STDERR,">&=",\*STDOUT or print ""|-": dup: $!"; exec "perl","-e",q[print "hi\n"; warn "bye\n"] or dIE ""-|": exec: $!";}

顺序不同,但请记住STDERR和STDOUT是不同的流.另请注意,只有“hi”才能获得前缀.要获得两条线,它们都必须出现在STDOUT上.

"-"

要构造管道,shell使用fork创建子进程,使用dup2执行重定向,并通过在适当的进程中调用exec来启动管道的每个阶段.对于上面的管道,过程类似于

> shell:fork一个进程来运行sed
> shell:使用waitpID等待sed的退出状态

> sed:创建一个管道以将输入提供给perl
> sed:fork一个进程运行perl
> sed:dup2使STDIN从管道的读取端读取
> sed:执行sed命令
> sed:等待STDIN的输入

> perl:dup2将STDOUT从步骤3发送到管道的写端
> perl:dup2将STDERR发送到STDOUT的目的地
> perl:exec perl命令
> perl:写输出并最终退出

> sed:接收和编辑输入流
> sed:检测管道上的文件结束
> sed:使用waitpID获取perl的退出状态
> sed:退出

> shell:填充$?使用waitpID的返回值

请注意,子进程是按从右到左的顺序创建的.这是因为Bourne系列中的shell将管道的退出状态定义为最后一个进程的退出状态.

自己动手管道

您可以使用下面的代码在Perl中构建上述管道.

$./simple-pipelinegot: byegot: hi

第一次打开电话为我们做了很多工作,如perlfunc documentation on

$./simple-pipeline >/dev/null
所述:

For three or more arguments if MODE is pp_backtick,the filename is interpreted as a command to which output is to be piped,and if MODE is Perl_my_popen,the filename is interpreted as a command that pipes output to us. In the two-argument (and one-argument) form,one should replace dash (/* handle the 2>&1 construct at the end */) with the command. See 07003 for more examples of this.

它的输出是

if (*s == '>' && s[1] == '&' && s[2] == '1' && s > cmd + 1 && s[-1] == '2' && isspace(s[-2]) && (!s[3] || isspace(s[3]))){ const char *t = s + 3; while (*t && isspace(*t)) ++t; if (!*t && (PerlliO_dup2(1,2) != -1)) { s[-2] = 'PerlProc_execl(PL_sh_path,"sh","-c",cmd,(char *)NulL);PERL_FPU_POST_EXECS_exec_Failed(aTHX_ PL_sh_path,fd,do_report);'; break; }}

上面的代码硬编码了STDOUT的复制,我们可以在下面看到.

if (ckWARN(WARN_EXEC)) Perl_warner(aTHX_ packWARN(WARN_EXEC),"Can't exec \"%s\": %s",Strerror(e));

Perl反引号

为了捕获另一个命令的输出,perl设置了相同的机制,你可以在

$perl -e 'use strict; use warnings; my $out=`DNE`; print $out'Can't exec "DNE": No such file or directory at -e line 1.Use of uninitialized value in print at -e line 1.
(在pp_sys.c中)看到它,它调用if (*s != ' ' && !isAlpha(*s) && strchr("$&*(){}[]'\";\|?<>~`\n",*s)) {(在util.c中)来创建子进程并设置管道(fork,管道,dup2).孩子做了一些管道并调用Perl_do_exec3(在doio.c中)来启动我们想要的输出命令.我们注意到a relevant comment:

$perl -e 'use strict; use warnings; my $out=`DNE 2>&1`; print $out'Use of uninitialized value in print at -e line 1.

该实现识别序列2>& 1,复制STDOUT,并从命令中移除重定向以传递给shell.

if (!*t && (PerlliO_dup2(1,2) != -1)) {    s[-2] = '
$perl -e 'use strict; use warnings; my $out=`echo 123; DNE 2>&1`; print $out'123sh: DNE: command not found
'; break;}

后来我们看到了

$perl -Mstrict -Mwarnings -e 'my $out=`DNE`; print $out'Can't exec "DNE": No such file or directory at -e line 1.Use of uninitialized value $out in print at -e line 1.$perl -Mstrict -Mwarnings -M-warnings=exec -e 'my $out=`DNE`; print $out'Use of uninitialized value $out in print at -e line 1.

在S_exec_Failed内部,我们发现

use strict;use warnings;no warnings 'exec';my $out = `DNE`;print defined($out) ? $out : "command Failed\n";

这是您在问题中提出的警告之一.

时间线

让我们详细了解perl如何处理您问题中的命令.

正如所料

这里没有惊喜.

一个微妙的细节是很重要的理解.仅当条件为要执行的命令为真时,上面处理2>& 1内部的代码才会运行:

这是一个优化.如果反引号中的命令包含上面的shell元字符,则perl必须将其移交给shell.但是如果没有shell元字符存在,perl可以直接执行命令 – 节省fork和shell启动成本.

不存在的命令DNE不包含shell元字符,因此perl完成所有工作.生成exec-category警告是因为命令失败并且您启用了警告编译指示. perlop文档告诉我们,当命令失败时,反引号或qx //在标量上下文中返回undef,这就是为什么你得到关于打印$out的未定义值的警告.

缺少警告

失败的执行警告在哪里?

请记住创建运行另一个命令的子进程的基本步骤:

>为子项创建管道以将其输出发送到父项.
>调用fork创建一个几乎相同的子进程.
>在子项中,dup2将STDOUT连接到管道的写入端.
>在子进程中,exec使新创建的子进程执行另一个程序.
>在父级中,读取管道的内容.

为了捕获另一个命令的输出,perl完成了这些步骤.在准备尝试运行DNE 2>& 1时,perl会分叉一个孩子并且在子进程中导致STDERR成为STDOUT的副本,但是存在另一个副作用.

[+++]

如果2>& 1在命令的末尾并且dup2成功,则perl在重定向之前写入Nul字节.这具有从命令中移除它的效果,例如,DNE 2>& 1变为DNE!现在,在命令中没有shell元字符的情况下,子进程中的perl认为自己是’Self,我们可以直接执行这个命令’.

对exec的调用失败,因为DNE不存在.孩子仍然在STDERR上发出失败的exec警告.由于dup2将STDERR指向与STDOUT相同的位置,因此它不会转到终端:管道的写入端返回到父节点.

父进程检测到子进程异常退出,并忽略管道内容,因为命令执行失败的结果记录为undef.

不同的警告

[+++]

在这里,我们看到不存在DNE的不同诊断.遇到的第一个shell元字符是;,因此perl将命令不变地移交给shell执行.回声正常完成,然后DNE在shell中失败,shell的STDOUT和STDERR返回到父进程.从perl的角度来看,shell执行得很好,所以没有什么值得警告的.

相关说明

当您启用警告pragma-a Very Good Practice! – 这将启用exec警告类别.要查看这些警告的完整列表,请在perldiag文档中搜索字符串W exec.

观察差异.

[+++]

后者的调用相当于

[+++]

我喜欢在执行exec,管道打开等问题时格式化我自己的错误消息.这意味着我通常禁用exec警告,但这也意味着我必须非常小心地测试返回值. 总结

以上是内存溢出为你收集整理的linux – 如果反引号不能执行,则STDER的STDERR重定向会丢失全部内容,希望文章能够帮你解决linux – 如果反引号不能执行,则STDER的STDERR重定向会丢失所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

)
File: /www/wwwroot/outofmemory.cn/tmp/route_read.php, Line: 126, InsideLink()
File: /www/wwwroot/outofmemory.cn/tmp/index.inc.php, Line: 166, include(/www/wwwroot/outofmemory.cn/tmp/route_read.php)
File: /www/wwwroot/outofmemory.cn/index.php, Line: 30, include(/www/wwwroot/outofmemory.cn/tmp/index.inc.php)
Error[8]: Undefined offset: 37, File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 121
File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 473, decode(

概述如果命令执行失败,我在反引号调用中发现STDERR重定向可能会丢失.我对我所看到的行为感到困惑.$perl -e 'use strict; use warnings; my $out=`DNE`; print $out' Can't exec 'DNE': No such file or directory at -e line 1. Use of un

如果命令执行失败,我在反引号调用中发现STDERR重定向可能会丢失.我对我所看到的行为感到困惑.

$perl -e 'use strict; use warnings; my $out=`DNE`; print $out'  Can't exec "DNE": No such file or directory at -e line 1.Use of uninitialized value in print at -e line 1.$perl -e 'use strict; use warnings; my $out=`DNE 2>&1`; print $out'Use of uninitialized value in print at -e line 1.$perl -e 'use strict; use warnings; my $out=`echo 123; DNE 2>&1`; print $out'123sh: DNE: command not found

我的语法不正确吗?

我在linux上使用Perl 5.8.5.

最佳答案您的语法是正确的,但在一种情况下perl正在删除错误消息.

通常,请考虑在初始化期间测试您的系统是否具有您想要的命令,如果缺少则请尽早失败.

my $foopath = "/usr/bin/foo";dIE "
perl -e 'print "hi\n"; warn "bye\n"'
: $foopath is not executable" unless -x $foopath;# later ...my $output = `$foopath 2>&1`;dIE "
hibye
: $foopath exited $?" if $?;

要完全理解输出的差异,有必要了解Unix系统编程的细节.继续阅读.

Unix文件描述符

考虑一个简单的perl调用.

$perl -e 'print "hi\n"; warn "bye\n"' >/dev/nullbye

它的输出是

$perl -e 'print "hi\n"; warn "bye\n"' 1>/dev/nullbye

请注意,print的输出转到标准输出STDOUT,并警告写入标准错误STDERR.从终端运行时,两者都出现在终端上,但我们可以将它们发送到不同的地方.例如

$perl -e 'print "hi\n"; warn "bye\n"' 2>/dev/nullhi

空设备或/ dev / null会丢弃发送给它的任何输出,因此在上面的命令中,“hi”消失.上面的命令是简写

$perl -e 'print "hi\n"; warn "bye\n"' 2>&1hibye

也就是说,1是STDOUT的文件描述符.相反,扔掉“再见”,跑

$perl -e 'print "hi\n"; warn "bye\n"' >/dev/null 2>&1

如您所见,2是STDERR的文件描述符. (为完整起见,STDIN的文件描述符为0.)

在Bourne shell及其衍生产品中,我们还可以将STDOUT和STDERR合并为2>& 1.将其读作“make file descriptor 2的输出与文件描述符1的相同位置”.

$perl -e 'print "hi\n"; warn "bye\n"' 2>&1 >/dev/nullbye

终端输出不突出显示区别,但额外的重定向显示正在发生的事情.我们可以通过跑步来丢弃它们

fcntl

订单与这个shell系列有关,它以从左到右的顺序处理重定向,因此转换两个产量

$perl -e 'print "hi\n"; warn "bye\n"' | sed -e 's/^/got: /'byegot: hi

起初这可能会令人惊讶. shell首先处理2>& 1,这意味着将STDERR发送到与STDOUT相同的目的地 – 它已经是:终端!然后它处理> / dev / null并将STDOUT重定向到空设备.

这种文件描述符的重复是通过调用@L_301_0@来实现的,这通常是

$perl -e 'print "hi\n"; warn "bye\n"' 2>&1 | sed -e 's/^/got: /'got: byegot: hi
的包装器.

重定向和管道

现在说我们要为命令输出的每一行添加一个前缀.

#! /usr/bin/env perluse strict;use warnings;my $pID = open my $fh,"-|";dIE "open: fork: $!" unless defined $pID;if ($pID) { while (<$fh>) { s/^/got: /; print; }}else { open STDERR,">&=",\*STDOUT or print ""|-": dup: $!"; exec "perl","-e",q[print "hi\n"; warn "bye\n"] or dIE ""-|": exec: $!";}

顺序不同,但请记住STDERR和STDOUT是不同的流.另请注意,只有“hi”才能获得前缀.要获得两条线,它们都必须出现在STDOUT上.

"-"

要构造管道,shell使用fork创建子进程,使用dup2执行重定向,并通过在适当的进程中调用exec来启动管道的每个阶段.对于上面的管道,过程类似于

> shell:fork一个进程来运行sed
> shell:使用waitpID等待sed的退出状态

> sed:创建一个管道以将输入提供给perl
> sed:fork一个进程运行perl
> sed:dup2使STDIN从管道的读取端读取
> sed:执行sed命令
> sed:等待STDIN的输入

> perl:dup2将STDOUT从步骤3发送到管道的写端
> perl:dup2将STDERR发送到STDOUT的目的地
> perl:exec perl命令
> perl:写输出并最终退出

> sed:接收和编辑输入流
> sed:检测管道上的文件结束
> sed:使用waitpID获取perl的退出状态
> sed:退出

> shell:填充$?使用waitpID的返回值

请注意,子进程是按从右到左的顺序创建的.这是因为Bourne系列中的shell将管道的退出状态定义为最后一个进程的退出状态.

自己动手管道

您可以使用下面的代码在Perl中构建上述管道.

$./simple-pipelinegot: byegot: hi

第一次打开电话为我们做了很多工作,如perlfunc documentation on

$./simple-pipeline >/dev/null
所述:

For three or more arguments if MODE is pp_backtick,the filename is interpreted as a command to which output is to be piped,and if MODE is Perl_my_popen,the filename is interpreted as a command that pipes output to us. In the two-argument (and one-argument) form,one should replace dash (/* handle the 2>&1 construct at the end */) with the command. See 07003 for more examples of this.

它的输出是

if (*s == '>' && s[1] == '&' && s[2] == '1' && s > cmd + 1 && s[-1] == '2' && isspace(s[-2]) && (!s[3] || isspace(s[3]))){ const char *t = s + 3; while (*t && isspace(*t)) ++t; if (!*t && (PerlliO_dup2(1,2) != -1)) { s[-2] = 'PerlProc_execl(PL_sh_path,"sh","-c",cmd,(char *)NulL);PERL_FPU_POST_EXECS_exec_Failed(aTHX_ PL_sh_path,fd,do_report);'; break; }}

上面的代码硬编码了STDOUT的复制,我们可以在下面看到.

if (ckWARN(WARN_EXEC)) Perl_warner(aTHX_ packWARN(WARN_EXEC),"Can't exec \"%s\": %s",Strerror(e));

Perl反引号

为了捕获另一个命令的输出,perl设置了相同的机制,你可以在

$perl -e 'use strict; use warnings; my $out=`DNE`; print $out'Can't exec "DNE": No such file or directory at -e line 1.Use of uninitialized value in print at -e line 1.
(在pp_sys.c中)看到它,它调用if (*s != ' ' && !isAlpha(*s) && strchr("$&*(){}[]'\";\|?<>~`\n",*s)) {(在util.c中)来创建子进程并设置管道(fork,管道,dup2).孩子做了一些管道并调用Perl_do_exec3(在doio.c中)来启动我们想要的输出命令.我们注意到a relevant comment:

$perl -e 'use strict; use warnings; my $out=`DNE 2>&1`; print $out'Use of uninitialized value in print at -e line 1.

该实现识别序列2>& 1,复制STDOUT,并从命令中移除重定向以传递给shell.

if (!*t && (PerlliO_dup2(1,2) != -1)) {    s[-2] = '
$perl -e 'use strict; use warnings; my $out=`echo 123; DNE 2>&1`; print $out'123sh: DNE: command not found
'; break;}

后来我们看到了

$perl -Mstrict -Mwarnings -e 'my $out=`DNE`; print $out'Can't exec "DNE": No such file or directory at -e line 1.Use of uninitialized value $out in print at -e line 1.$perl -Mstrict -Mwarnings -M-warnings=exec -e 'my $out=`DNE`; print $out'Use of uninitialized value $out in print at -e line 1.

在S_exec_Failed内部,我们发现

use strict;use warnings;no warnings 'exec';my $out = `DNE`;print defined($out) ? $out : "command Failed\n";

这是您在问题中提出的警告之一.

时间线

让我们详细了解perl如何处理您问题中的命令.

正如所料

这里没有惊喜.

一个微妙的细节是很重要的理解.仅当条件为要执行的命令为真时,上面处理2>& 1内部的代码才会运行:

这是一个优化.如果反引号中的命令包含上面的shell元字符,则perl必须将其移交给shell.但是如果没有shell元字符存在,perl可以直接执行命令 – 节省fork和shell启动成本.

不存在的命令DNE不包含shell元字符,因此perl完成所有工作.生成exec-category警告是因为命令失败并且您启用了警告编译指示. perlop文档告诉我们,当命令失败时,反引号或qx //在标量上下文中返回undef,这就是为什么你得到关于打印$out的未定义值的警告.

缺少警告

失败的执行警告在哪里?

请记住创建运行另一个命令的子进程的基本步骤:

>为子项创建管道以将其输出发送到父项.
>调用fork创建一个几乎相同的子进程.
>在子项中,dup2将STDOUT连接到管道的写入端.
>在子进程中,exec使新创建的子进程执行另一个程序.
>在父级中,读取管道的内容.

为了捕获另一个命令的输出,perl完成了这些步骤.在准备尝试运行DNE 2>& 1时,perl会分叉一个孩子并且在子进程中导致STDERR成为STDOUT的副本,但是存在另一个副作用.

如果2>& 1在命令的末尾并且dup2成功,则perl在重定向之前写入Nul字节.这具有从命令中移除它的效果,例如,DNE 2>& 1变为DNE!现在,在命令中没有shell元字符的情况下,子进程中的perl认为自己是’Self,我们可以直接执行这个命令’.

对exec的调用失败,因为DNE不存在.孩子仍然在STDERR上发出失败的exec警告.由于dup2将STDERR指向与STDOUT相同的位置,因此它不会转到终端:管道的写入端返回到父节点.

父进程检测到子进程异常退出,并忽略管道内容,因为命令执行失败的结果记录为undef.

不同的警告

[+++]

在这里,我们看到不存在DNE的不同诊断.遇到的第一个shell元字符是;,因此perl将命令不变地移交给shell执行.回声正常完成,然后DNE在shell中失败,shell的STDOUT和STDERR返回到父进程.从perl的角度来看,shell执行得很好,所以没有什么值得警告的.

相关说明

当您启用警告pragma-a Very Good Practice! – 这将启用exec警告类别.要查看这些警告的完整列表,请在perldiag文档中搜索字符串W exec.

观察差异.

[+++]

后者的调用相当于

[+++]

我喜欢在执行exec,管道打开等问题时格式化我自己的错误消息.这意味着我通常禁用exec警告,但这也意味着我必须非常小心地测试返回值. 总结

以上是内存溢出为你收集整理的linux – 如果反引号不能执行,则STDER的STDERR重定向会丢失全部内容,希望文章能够帮你解决linux – 如果反引号不能执行,则STDER的STDERR重定向会丢失所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

)
File: /www/wwwroot/outofmemory.cn/tmp/route_read.php, Line: 126, InsideLink()
File: /www/wwwroot/outofmemory.cn/tmp/index.inc.php, Line: 166, include(/www/wwwroot/outofmemory.cn/tmp/route_read.php)
File: /www/wwwroot/outofmemory.cn/index.php, Line: 30, include(/www/wwwroot/outofmemory.cn/tmp/index.inc.php)
Error[8]: Undefined offset: 38, File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 121
File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 473, decode(

概述如果命令执行失败,我在反引号调用中发现STDERR重定向可能会丢失.我对我所看到的行为感到困惑.$perl -e 'use strict; use warnings; my $out=`DNE`; print $out' Can't exec 'DNE': No such file or directory at -e line 1. Use of un

如果命令执行失败,我在反引号调用中发现STDERR重定向可能会丢失.我对我所看到的行为感到困惑.

$perl -e 'use strict; use warnings; my $out=`DNE`; print $out'  Can't exec "DNE": No such file or directory at -e line 1.Use of uninitialized value in print at -e line 1.$perl -e 'use strict; use warnings; my $out=`DNE 2>&1`; print $out'Use of uninitialized value in print at -e line 1.$perl -e 'use strict; use warnings; my $out=`echo 123; DNE 2>&1`; print $out'123sh: DNE: command not found

我的语法不正确吗?

我在linux上使用Perl 5.8.5.

最佳答案您的语法是正确的,但在一种情况下perl正在删除错误消息.

通常,请考虑在初始化期间测试您的系统是否具有您想要的命令,如果缺少则请尽早失败.

my $foopath = "/usr/bin/foo";dIE "
perl -e 'print "hi\n"; warn "bye\n"'
: $foopath is not executable" unless -x $foopath;# later ...my $output = `$foopath 2>&1`;dIE "
hibye
: $foopath exited $?" if $?;

要完全理解输出的差异,有必要了解Unix系统编程的细节.继续阅读.

Unix文件描述符

考虑一个简单的perl调用.

$perl -e 'print "hi\n"; warn "bye\n"' >/dev/nullbye

它的输出是

$perl -e 'print "hi\n"; warn "bye\n"' 1>/dev/nullbye

请注意,print的输出转到标准输出STDOUT,并警告写入标准错误STDERR.从终端运行时,两者都出现在终端上,但我们可以将它们发送到不同的地方.例如

$perl -e 'print "hi\n"; warn "bye\n"' 2>/dev/nullhi

空设备或/ dev / null会丢弃发送给它的任何输出,因此在上面的命令中,“hi”消失.上面的命令是简写

$perl -e 'print "hi\n"; warn "bye\n"' 2>&1hibye

也就是说,1是STDOUT的文件描述符.相反,扔掉“再见”,跑

$perl -e 'print "hi\n"; warn "bye\n"' >/dev/null 2>&1

如您所见,2是STDERR的文件描述符. (为完整起见,STDIN的文件描述符为0.)

在Bourne shell及其衍生产品中,我们还可以将STDOUT和STDERR合并为2>& 1.将其读作“make file descriptor 2的输出与文件描述符1的相同位置”.

$perl -e 'print "hi\n"; warn "bye\n"' 2>&1 >/dev/nullbye

终端输出不突出显示区别,但额外的重定向显示正在发生的事情.我们可以通过跑步来丢弃它们

fcntl

订单与这个shell系列有关,它以从左到右的顺序处理重定向,因此转换两个产量

$perl -e 'print "hi\n"; warn "bye\n"' | sed -e 's/^/got: /'byegot: hi

起初这可能会令人惊讶. shell首先处理2>& 1,这意味着将STDERR发送到与STDOUT相同的目的地 – 它已经是:终端!然后它处理> / dev / null并将STDOUT重定向到空设备.

这种文件描述符的重复是通过调用@L_301_0@来实现的,这通常是

$perl -e 'print "hi\n"; warn "bye\n"' 2>&1 | sed -e 's/^/got: /'got: byegot: hi
的包装器.

重定向和管道

现在说我们要为命令输出的每一行添加一个前缀.

#! /usr/bin/env perluse strict;use warnings;my $pID = open my $fh,"-|";dIE "open: fork: $!" unless defined $pID;if ($pID) { while (<$fh>) { s/^/got: /; print; }}else { open STDERR,">&=",\*STDOUT or print ""|-": dup: $!"; exec "perl","-e",q[print "hi\n"; warn "bye\n"] or dIE ""-|": exec: $!";}

顺序不同,但请记住STDERR和STDOUT是不同的流.另请注意,只有“hi”才能获得前缀.要获得两条线,它们都必须出现在STDOUT上.

"-"

要构造管道,shell使用fork创建子进程,使用dup2执行重定向,并通过在适当的进程中调用exec来启动管道的每个阶段.对于上面的管道,过程类似于

> shell:fork一个进程来运行sed
> shell:使用waitpID等待sed的退出状态

> sed:创建一个管道以将输入提供给perl
> sed:fork一个进程运行perl
> sed:dup2使STDIN从管道的读取端读取
> sed:执行sed命令
> sed:等待STDIN的输入

> perl:dup2将STDOUT从步骤3发送到管道的写端
> perl:dup2将STDERR发送到STDOUT的目的地
> perl:exec perl命令
> perl:写输出并最终退出

> sed:接收和编辑输入流
> sed:检测管道上的文件结束
> sed:使用waitpID获取perl的退出状态
> sed:退出

> shell:填充$?使用waitpID的返回值

请注意,子进程是按从右到左的顺序创建的.这是因为Bourne系列中的shell将管道的退出状态定义为最后一个进程的退出状态.

自己动手管道

您可以使用下面的代码在Perl中构建上述管道.

$./simple-pipelinegot: byegot: hi

第一次打开电话为我们做了很多工作,如perlfunc documentation on

$./simple-pipeline >/dev/null
所述:

For three or more arguments if MODE is pp_backtick,the filename is interpreted as a command to which output is to be piped,and if MODE is Perl_my_popen,the filename is interpreted as a command that pipes output to us. In the two-argument (and one-argument) form,one should replace dash (/* handle the 2>&1 construct at the end */) with the command. See 07003 for more examples of this.

它的输出是

if (*s == '>' && s[1] == '&' && s[2] == '1' && s > cmd + 1 && s[-1] == '2' && isspace(s[-2]) && (!s[3] || isspace(s[3]))){ const char *t = s + 3; while (*t && isspace(*t)) ++t; if (!*t && (PerlliO_dup2(1,2) != -1)) { s[-2] = 'PerlProc_execl(PL_sh_path,"sh","-c",cmd,(char *)NulL);PERL_FPU_POST_EXECS_exec_Failed(aTHX_ PL_sh_path,fd,do_report);'; break; }}

上面的代码硬编码了STDOUT的复制,我们可以在下面看到.

if (ckWARN(WARN_EXEC)) Perl_warner(aTHX_ packWARN(WARN_EXEC),"Can't exec \"%s\": %s",Strerror(e));

Perl反引号

为了捕获另一个命令的输出,perl设置了相同的机制,你可以在

$perl -e 'use strict; use warnings; my $out=`DNE`; print $out'Can't exec "DNE": No such file or directory at -e line 1.Use of uninitialized value in print at -e line 1.
(在pp_sys.c中)看到它,它调用if (*s != ' ' && !isAlpha(*s) && strchr("$&*(){}[]'\";\|?<>~`\n",*s)) {(在util.c中)来创建子进程并设置管道(fork,管道,dup2).孩子做了一些管道并调用Perl_do_exec3(在doio.c中)来启动我们想要的输出命令.我们注意到a relevant comment:

$perl -e 'use strict; use warnings; my $out=`DNE 2>&1`; print $out'Use of uninitialized value in print at -e line 1.

该实现识别序列2>& 1,复制STDOUT,并从命令中移除重定向以传递给shell.

if (!*t && (PerlliO_dup2(1,2) != -1)) {    s[-2] = '
$perl -e 'use strict; use warnings; my $out=`echo 123; DNE 2>&1`; print $out'123sh: DNE: command not found
'; break;}

后来我们看到了

$perl -Mstrict -Mwarnings -e 'my $out=`DNE`; print $out'Can't exec "DNE": No such file or directory at -e line 1.Use of uninitialized value $out in print at -e line 1.$perl -Mstrict -Mwarnings -M-warnings=exec -e 'my $out=`DNE`; print $out'Use of uninitialized value $out in print at -e line 1.

在S_exec_Failed内部,我们发现

use strict;use warnings;no warnings 'exec';my $out = `DNE`;print defined($out) ? $out : "command Failed\n";

这是您在问题中提出的警告之一.

时间线

让我们详细了解perl如何处理您问题中的命令.

正如所料

这里没有惊喜.

一个微妙的细节是很重要的理解.仅当条件为要执行的命令为真时,上面处理2>& 1内部的代码才会运行:

这是一个优化.如果反引号中的命令包含上面的shell元字符,则perl必须将其移交给shell.但是如果没有shell元字符存在,perl可以直接执行命令 – 节省fork和shell启动成本.

不存在的命令DNE不包含shell元字符,因此perl完成所有工作.生成exec-category警告是因为命令失败并且您启用了警告编译指示. perlop文档告诉我们,当命令失败时,反引号或qx //在标量上下文中返回undef,这就是为什么你得到关于打印$out的未定义值的警告.

缺少警告

失败的执行警告在哪里?

请记住创建运行另一个命令的子进程的基本步骤:

>为子项创建管道以将其输出发送到父项.
>调用fork创建一个几乎相同的子进程.
>在子项中,dup2将STDOUT连接到管道的写入端.
>在子进程中,exec使新创建的子进程执行另一个程序.
>在父级中,读取管道的内容.

为了捕获另一个命令的输出,perl完成了这些步骤.在准备尝试运行DNE 2>& 1时,perl会分叉一个孩子并且在子进程中导致STDERR成为STDOUT的副本,但是存在另一个副作用.

如果2>& 1在命令的末尾并且dup2成功,则perl在重定向之前写入Nul字节.这具有从命令中移除它的效果,例如,DNE 2>& 1变为DNE!现在,在命令中没有shell元字符的情况下,子进程中的perl认为自己是’Self,我们可以直接执行这个命令’.

对exec的调用失败,因为DNE不存在.孩子仍然在STDERR上发出失败的exec警告.由于dup2将STDERR指向与STDOUT相同的位置,因此它不会转到终端:管道的写入端返回到父节点.

父进程检测到子进程异常退出,并忽略管道内容,因为命令执行失败的结果记录为undef.

不同的警告

在这里,我们看到不存在DNE的不同诊断.遇到的第一个shell元字符是;,因此perl将命令不变地移交给shell执行.回声正常完成,然后DNE在shell中失败,shell的STDOUT和STDERR返回到父进程.从perl的角度来看,shell执行得很好,所以没有什么值得警告的.

相关说明

当您启用警告pragma-a Very Good Practice! – 这将启用exec警告类别.要查看这些警告的完整列表,请在perldiag文档中搜索字符串W exec.

观察差异.

[+++]

后者的调用相当于

[+++]

我喜欢在执行exec,管道打开等问题时格式化我自己的错误消息.这意味着我通常禁用exec警告,但这也意味着我必须非常小心地测试返回值. 总结

以上是内存溢出为你收集整理的linux – 如果反引号不能执行,则STDER的STDERR重定向会丢失全部内容,希望文章能够帮你解决linux – 如果反引号不能执行,则STDER的STDERR重定向会丢失所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

)
File: /www/wwwroot/outofmemory.cn/tmp/route_read.php, Line: 126, InsideLink()
File: /www/wwwroot/outofmemory.cn/tmp/index.inc.php, Line: 166, include(/www/wwwroot/outofmemory.cn/tmp/route_read.php)
File: /www/wwwroot/outofmemory.cn/index.php, Line: 30, include(/www/wwwroot/outofmemory.cn/tmp/index.inc.php)
Error[8]: Undefined offset: 39, File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 121
File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 473, decode(

概述如果命令执行失败,我在反引号调用中发现STDERR重定向可能会丢失.我对我所看到的行为感到困惑.$perl -e 'use strict; use warnings; my $out=`DNE`; print $out' Can't exec 'DNE': No such file or directory at -e line 1. Use of un

如果命令执行失败,我在反引号调用中发现STDERR重定向可能会丢失.我对我所看到的行为感到困惑.

$perl -e 'use strict; use warnings; my $out=`DNE`; print $out'  Can't exec "DNE": No such file or directory at -e line 1.Use of uninitialized value in print at -e line 1.$perl -e 'use strict; use warnings; my $out=`DNE 2>&1`; print $out'Use of uninitialized value in print at -e line 1.$perl -e 'use strict; use warnings; my $out=`echo 123; DNE 2>&1`; print $out'123sh: DNE: command not found

我的语法不正确吗?

我在linux上使用Perl 5.8.5.

最佳答案您的语法是正确的,但在一种情况下perl正在删除错误消息.

通常,请考虑在初始化期间测试您的系统是否具有您想要的命令,如果缺少则请尽早失败.

my $foopath = "/usr/bin/foo";dIE "
perl -e 'print "hi\n"; warn "bye\n"'
: $foopath is not executable" unless -x $foopath;# later ...my $output = `$foopath 2>&1`;dIE "
hibye
: $foopath exited $?" if $?;

要完全理解输出的差异,有必要了解Unix系统编程的细节.继续阅读.

Unix文件描述符

考虑一个简单的perl调用.

$perl -e 'print "hi\n"; warn "bye\n"' >/dev/nullbye

它的输出是

$perl -e 'print "hi\n"; warn "bye\n"' 1>/dev/nullbye

请注意,print的输出转到标准输出STDOUT,并警告写入标准错误STDERR.从终端运行时,两者都出现在终端上,但我们可以将它们发送到不同的地方.例如

$perl -e 'print "hi\n"; warn "bye\n"' 2>/dev/nullhi

空设备或/ dev / null会丢弃发送给它的任何输出,因此在上面的命令中,“hi”消失.上面的命令是简写

$perl -e 'print "hi\n"; warn "bye\n"' 2>&1hibye

也就是说,1是STDOUT的文件描述符.相反,扔掉“再见”,跑

$perl -e 'print "hi\n"; warn "bye\n"' >/dev/null 2>&1

如您所见,2是STDERR的文件描述符. (为完整起见,STDIN的文件描述符为0.)

在Bourne shell及其衍生产品中,我们还可以将STDOUT和STDERR合并为2>& 1.将其读作“make file descriptor 2的输出与文件描述符1的相同位置”.

$perl -e 'print "hi\n"; warn "bye\n"' 2>&1 >/dev/nullbye

终端输出不突出显示区别,但额外的重定向显示正在发生的事情.我们可以通过跑步来丢弃它们

fcntl

订单与这个shell系列有关,它以从左到右的顺序处理重定向,因此转换两个产量

$perl -e 'print "hi\n"; warn "bye\n"' | sed -e 's/^/got: /'byegot: hi

起初这可能会令人惊讶. shell首先处理2>& 1,这意味着将STDERR发送到与STDOUT相同的目的地 – 它已经是:终端!然后它处理> / dev / null并将STDOUT重定向到空设备.

这种文件描述符的重复是通过调用@L_301_0@来实现的,这通常是

$perl -e 'print "hi\n"; warn "bye\n"' 2>&1 | sed -e 's/^/got: /'got: byegot: hi
的包装器.

重定向和管道

现在说我们要为命令输出的每一行添加一个前缀.

#! /usr/bin/env perluse strict;use warnings;my $pID = open my $fh,"-|";dIE "open: fork: $!" unless defined $pID;if ($pID) { while (<$fh>) { s/^/got: /; print; }}else { open STDERR,">&=",\*STDOUT or print ""|-": dup: $!"; exec "perl","-e",q[print "hi\n"; warn "bye\n"] or dIE ""-|": exec: $!";}

顺序不同,但请记住STDERR和STDOUT是不同的流.另请注意,只有“hi”才能获得前缀.要获得两条线,它们都必须出现在STDOUT上.

"-"

要构造管道,shell使用fork创建子进程,使用dup2执行重定向,并通过在适当的进程中调用exec来启动管道的每个阶段.对于上面的管道,过程类似于

> shell:fork一个进程来运行sed
> shell:使用waitpID等待sed的退出状态

> sed:创建一个管道以将输入提供给perl
> sed:fork一个进程运行perl
> sed:dup2使STDIN从管道的读取端读取
> sed:执行sed命令
> sed:等待STDIN的输入

> perl:dup2将STDOUT从步骤3发送到管道的写端
> perl:dup2将STDERR发送到STDOUT的目的地
> perl:exec perl命令
> perl:写输出并最终退出

> sed:接收和编辑输入流
> sed:检测管道上的文件结束
> sed:使用waitpID获取perl的退出状态
> sed:退出

> shell:填充$?使用waitpID的返回值

请注意,子进程是按从右到左的顺序创建的.这是因为Bourne系列中的shell将管道的退出状态定义为最后一个进程的退出状态.

自己动手管道

您可以使用下面的代码在Perl中构建上述管道.

$./simple-pipelinegot: byegot: hi

第一次打开电话为我们做了很多工作,如perlfunc documentation on

$./simple-pipeline >/dev/null
所述:

For three or more arguments if MODE is pp_backtick,the filename is interpreted as a command to which output is to be piped,and if MODE is Perl_my_popen,the filename is interpreted as a command that pipes output to us. In the two-argument (and one-argument) form,one should replace dash (/* handle the 2>&1 construct at the end */) with the command. See 07003 for more examples of this.

它的输出是

if (*s == '>' && s[1] == '&' && s[2] == '1' && s > cmd + 1 && s[-1] == '2' && isspace(s[-2]) && (!s[3] || isspace(s[3]))){ const char *t = s + 3; while (*t && isspace(*t)) ++t; if (!*t && (PerlliO_dup2(1,2) != -1)) { s[-2] = 'PerlProc_execl(PL_sh_path,"sh","-c",cmd,(char *)NulL);PERL_FPU_POST_EXECS_exec_Failed(aTHX_ PL_sh_path,fd,do_report);'; break; }}

上面的代码硬编码了STDOUT的复制,我们可以在下面看到.

if (ckWARN(WARN_EXEC)) Perl_warner(aTHX_ packWARN(WARN_EXEC),"Can't exec \"%s\": %s",Strerror(e));

Perl反引号

为了捕获另一个命令的输出,perl设置了相同的机制,你可以在

$perl -e 'use strict; use warnings; my $out=`DNE`; print $out'Can't exec "DNE": No such file or directory at -e line 1.Use of uninitialized value in print at -e line 1.
(在pp_sys.c中)看到它,它调用if (*s != ' ' && !isAlpha(*s) && strchr("$&*(){}[]'\";\|?<>~`\n",*s)) {(在util.c中)来创建子进程并设置管道(fork,管道,dup2).孩子做了一些管道并调用Perl_do_exec3(在doio.c中)来启动我们想要的输出命令.我们注意到a relevant comment:

$perl -e 'use strict; use warnings; my $out=`DNE 2>&1`; print $out'Use of uninitialized value in print at -e line 1.

该实现识别序列2>& 1,复制STDOUT,并从命令中移除重定向以传递给shell.

if (!*t && (PerlliO_dup2(1,2) != -1)) {    s[-2] = '
$perl -e 'use strict; use warnings; my $out=`echo 123; DNE 2>&1`; print $out'123sh: DNE: command not found
'; break;}

后来我们看到了

$perl -Mstrict -Mwarnings -e 'my $out=`DNE`; print $out'Can't exec "DNE": No such file or directory at -e line 1.Use of uninitialized value $out in print at -e line 1.$perl -Mstrict -Mwarnings -M-warnings=exec -e 'my $out=`DNE`; print $out'Use of uninitialized value $out in print at -e line 1.

在S_exec_Failed内部,我们发现

use strict;use warnings;no warnings 'exec';my $out = `DNE`;print defined($out) ? $out : "command Failed\n";

这是您在问题中提出的警告之一.

时间线

让我们详细了解perl如何处理您问题中的命令.

正如所料

这里没有惊喜.

一个微妙的细节是很重要的理解.仅当条件为要执行的命令为真时,上面处理2>& 1内部的代码才会运行:

这是一个优化.如果反引号中的命令包含上面的shell元字符,则perl必须将其移交给shell.但是如果没有shell元字符存在,perl可以直接执行命令 – 节省fork和shell启动成本.

不存在的命令DNE不包含shell元字符,因此perl完成所有工作.生成exec-category警告是因为命令失败并且您启用了警告编译指示. perlop文档告诉我们,当命令失败时,反引号或qx //在标量上下文中返回undef,这就是为什么你得到关于打印$out的未定义值的警告.

缺少警告

失败的执行警告在哪里?

请记住创建运行另一个命令的子进程的基本步骤:

>为子项创建管道以将其输出发送到父项.
>调用fork创建一个几乎相同的子进程.
>在子项中,dup2将STDOUT连接到管道的写入端.
>在子进程中,exec使新创建的子进程执行另一个程序.
>在父级中,读取管道的内容.

为了捕获另一个命令的输出,perl完成了这些步骤.在准备尝试运行DNE 2>& 1时,perl会分叉一个孩子并且在子进程中导致STDERR成为STDOUT的副本,但是存在另一个副作用.

如果2>& 1在命令的末尾并且dup2成功,则perl在重定向之前写入Nul字节.这具有从命令中移除它的效果,例如,DNE 2>& 1变为DNE!现在,在命令中没有shell元字符的情况下,子进程中的perl认为自己是’Self,我们可以直接执行这个命令’.

对exec的调用失败,因为DNE不存在.孩子仍然在STDERR上发出失败的exec警告.由于dup2将STDERR指向与STDOUT相同的位置,因此它不会转到终端:管道的写入端返回到父节点.

父进程检测到子进程异常退出,并忽略管道内容,因为命令执行失败的结果记录为undef.

不同的警告

在这里,我们看到不存在DNE的不同诊断.遇到的第一个shell元字符是;,因此perl将命令不变地移交给shell执行.回声正常完成,然后DNE在shell中失败,shell的STDOUT和STDERR返回到父进程.从perl的角度来看,shell执行得很好,所以没有什么值得警告的.

相关说明

当您启用警告pragma-a Very Good Practice! – 这将启用exec警告类别.要查看这些警告的完整列表,请在perldiag文档中搜索字符串W exec.

观察差异.

后者的调用相当于

[+++]

我喜欢在执行exec,管道打开等问题时格式化我自己的错误消息.这意味着我通常禁用exec警告,但这也意味着我必须非常小心地测试返回值. 总结

以上是内存溢出为你收集整理的linux – 如果反引号不能执行,则STDER的STDERR重定向会丢失全部内容,希望文章能够帮你解决linux – 如果反引号不能执行,则STDER的STDERR重定向会丢失所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

)
File: /www/wwwroot/outofmemory.cn/tmp/route_read.php, Line: 126, InsideLink()
File: /www/wwwroot/outofmemory.cn/tmp/index.inc.php, Line: 166, include(/www/wwwroot/outofmemory.cn/tmp/route_read.php)
File: /www/wwwroot/outofmemory.cn/index.php, Line: 30, include(/www/wwwroot/outofmemory.cn/tmp/index.inc.php)
linux– 如果反引号不能执行,则STDER的STDERR重定向会丢失_系统运维_内存溢出

linux– 如果反引号不能执行,则STDER的STDERR重定向会丢失

linux– 如果反引号不能执行,则STDER的STDERR重定向会丢失,第1张

概述如果命令执行失败,我在反引号调用中发现STDERR重定向可能会丢失.我对我所看到的行为感到困惑.$perl -e 'use strict; use warnings; my $out=`DNE`; print $out' Can't exec 'DNE': No such file or directory at -e line 1. Use of un

如果命令执行失败,我在反引号调用中发现STDERR重定向可能会丢失.我对我所看到的行为感到困惑.

$perl -e 'use strict; use warnings; my $out=`DNE`; print $out'  Can't exec "DNE": No such file or directory at -e line 1.Use of uninitialized value in print at -e line 1.$perl -e 'use strict; use warnings; my $out=`DNE 2>&1`; print $out'Use of uninitialized value in print at -e line 1.$perl -e 'use strict; use warnings; my $out=`echo 123; DNE 2>&1`; print $out'123sh: DNE: command not found

我的语法不正确吗?

我在linux上使用Perl 5.8.5.

最佳答案您的语法是正确的,但在一种情况下perl正在删除错误消息.

通常,请考虑在初始化期间测试您的系统是否具有您想要的命令,如果缺少则请尽早失败.

my $foopath = "/usr/bin/foo";dIE "
perl -e 'print "hi\n"; warn "bye\n"'
: $foopath is not executable" unless -x $foopath;# later ...my $output = `$foopath 2>&1`;dIE "
hibye
: $foopath exited $?" if $?;

要完全理解输出的差异,有必要了解Unix系统编程的细节.继续阅读.

Unix文件描述符

考虑一个简单的perl调用.

$perl -e 'print "hi\n"; warn "bye\n"' >/dev/nullbye

它的输出是

$perl -e 'print "hi\n"; warn "bye\n"' 1>/dev/nullbye

请注意,print的输出转到标准输出STDOUT,并警告写入标准错误STDERR.从终端运行时,两者都出现在终端上,但我们可以将它们发送到不同的地方.例如

$perl -e 'print "hi\n"; warn "bye\n"' 2>/dev/nullhi

空设备或/ dev / null会丢弃发送给它的任何输出,因此在上面的命令中,“hi”消失.上面的命令是简写

$perl -e 'print "hi\n"; warn "bye\n"' 2>&1hibye

也就是说,1是STDOUT的文件描述符.相反,扔掉“再见”,跑

$perl -e 'print "hi\n"; warn "bye\n"' >/dev/null 2>&1

如您所见,2是STDERR的文件描述符. (为完整起见,STDIN的文件描述符为0.)

在Bourne shell及其衍生产品中,我们还可以将STDOUT和STDERR合并为2>& 1.将其读作“make file descriptor 2的输出与文件描述符1的相同位置”.

$perl -e 'print "hi\n"; warn "bye\n"' 2>&1 >/dev/nullbye

终端输出不突出显示区别,但额外的重定向显示正在发生的事情.我们可以通过跑步来丢弃它们

fcntl

订单与这个shell系列有关,它以从左到右的顺序处理重定向,因此转换两个产量

$perl -e 'print "hi\n"; warn "bye\n"' | sed -e 's/^/got: /'byegot: hi

起初这可能会令人惊讶. shell首先处理2>& 1,这意味着将STDERR发送到与STDOUT相同的目的地 – 它已经是:终端!然后它处理> / dev / null并将STDOUT重定向到空设备.

这种文件描述符的重复是通过调用@L_301_0@来实现的,这通常是

$perl -e 'print "hi\n"; warn "bye\n"' 2>&1 | sed -e 's/^/got: /'got: byegot: hi
的包装器.

重定向和管道

现在说我们要为命令输出的每一行添加一个前缀.

#! /usr/bin/env perluse strict;use warnings;my $pID = open my $fh,"-|";dIE "open: fork: $!" unless defined $pID;if ($pID) { while (<$fh>) { s/^/got: /; print; }}else { open STDERR,">&=",\*STDOUT or print ""|-": dup: $!"; exec "perl","-e",q[print "hi\n"; warn "bye\n"] or dIE ""-|": exec: $!";}

顺序不同,但请记住STDERR和STDOUT是不同的流.另请注意,只有“hi”才能获得前缀.要获得两条线,它们都必须出现在STDOUT上.

"-"

要构造管道,shell使用fork创建子进程,使用dup2执行重定向,并通过在适当的进程中调用exec来启动管道的每个阶段.对于上面的管道,过程类似于

> shell:fork一个进程来运行sed
> shell:使用waitpID等待sed的退出状态

> sed:创建一个管道以将输入提供给perl
> sed:fork一个进程运行perl
> sed:dup2使STDIN从管道的读取端读取
> sed:执行sed命令
> sed:等待STDIN的输入

> perl:dup2将STDOUT从步骤3发送到管道的写端
> perl:dup2将STDERR发送到STDOUT的目的地
> perl:exec perl命令
> perl:写输出并最终退出

> sed:接收和编辑输入流
> sed:检测管道上的文件结束
> sed:使用waitpID获取perl的退出状态
> sed:退出

> shell:填充$?使用waitpID的返回值

请注意,子进程是按从右到左的顺序创建的.这是因为Bourne系列中的shell将管道的退出状态定义为最后一个进程的退出状态.

自己动手管道

您可以使用下面的代码在Perl中构建上述管道.

$./simple-pipelinegot: byegot: hi

第一次打开电话为我们做了很多工作,如perlfunc documentation on

$./simple-pipeline >/dev/null
所述:

For three or more arguments if MODE is pp_backtick,the filename is interpreted as a command to which output is to be piped,and if MODE is Perl_my_popen,the filename is interpreted as a command that pipes output to us. In the two-argument (and one-argument) form,one should replace dash (/* handle the 2>&1 construct at the end */) with the command. See 07003 for more examples of this.

它的输出是

if (*s == '>' && s[1] == '&' && s[2] == '1' && s > cmd + 1 && s[-1] == '2' && isspace(s[-2]) && (!s[3] || isspace(s[3]))){ const char *t = s + 3; while (*t && isspace(*t)) ++t; if (!*t && (PerlliO_dup2(1,2) != -1)) { s[-2] = 'PerlProc_execl(PL_sh_path,"sh","-c",cmd,(char *)NulL);PERL_FPU_POST_EXECS_exec_Failed(aTHX_ PL_sh_path,fd,do_report);'; break; }}

上面的代码硬编码了STDOUT的复制,我们可以在下面看到.

if (ckWARN(WARN_EXEC)) Perl_warner(aTHX_ packWARN(WARN_EXEC),"Can't exec \"%s\": %s",Strerror(e));

Perl反引号

为了捕获另一个命令的输出,perl设置了相同的机制,你可以在

$perl -e 'use strict; use warnings; my $out=`DNE`; print $out'Can't exec "DNE": No such file or directory at -e line 1.Use of uninitialized value in print at -e line 1.
(在pp_sys.c中)看到它,它调用if (*s != ' ' && !isAlpha(*s) && strchr("$&*(){}[]'\";\|?<>~`\n",*s)) {(在util.c中)来创建子进程并设置管道(fork,管道,dup2).孩子做了一些管道并调用Perl_do_exec3(在doio.c中)来启动我们想要的输出命令.我们注意到a relevant comment:

$perl -e 'use strict; use warnings; my $out=`DNE 2>&1`; print $out'Use of uninitialized value in print at -e line 1.

该实现识别序列2>& 1,复制STDOUT,并从命令中移除重定向以传递给shell.

if (!*t && (PerlliO_dup2(1,2) != -1)) {    s[-2] = '
$perl -e 'use strict; use warnings; my $out=`echo 123; DNE 2>&1`; print $out'123sh: DNE: command not found
'; break;}

后来我们看到了

$perl -Mstrict -Mwarnings -e 'my $out=`DNE`; print $out'Can't exec "DNE": No such file or directory at -e line 1.Use of uninitialized value $out in print at -e line 1.$perl -Mstrict -Mwarnings -M-warnings=exec -e 'my $out=`DNE`; print $out'Use of uninitialized value $out in print at -e line 1.

在S_exec_Failed内部,我们发现

use strict;use warnings;no warnings 'exec';my $out = `DNE`;print defined($out) ? $out : "command Failed\n";

这是您在问题中提出的警告之一.

时间线

让我们详细了解perl如何处理您问题中的命令.

正如所料

这里没有惊喜.

一个微妙的细节是很重要的理解.仅当条件为要执行的命令为真时,上面处理2>& 1内部的代码才会运行:

这是一个优化.如果反引号中的命令包含上面的shell元字符,则perl必须将其移交给shell.但是如果没有shell元字符存在,perl可以直接执行命令 – 节省fork和shell启动成本.

不存在的命令DNE不包含shell元字符,因此perl完成所有工作.生成exec-category警告是因为命令失败并且您启用了警告编译指示. perlop文档告诉我们,当命令失败时,反引号或qx //在标量上下文中返回undef,这就是为什么你得到关于打印$out的未定义值的警告.

缺少警告

失败的执行警告在哪里?

请记住创建运行另一个命令的子进程的基本步骤:

>为子项创建管道以将其输出发送到父项.
>调用fork创建一个几乎相同的子进程.
>在子项中,dup2将STDOUT连接到管道的写入端.
>在子进程中,exec使新创建的子进程执行另一个程序.
>在父级中,读取管道的内容.

为了捕获另一个命令的输出,perl完成了这些步骤.在准备尝试运行DNE 2>& 1时,perl会分叉一个孩子并且在子进程中导致STDERR成为STDOUT的副本,但是存在另一个副作用.

如果2>& 1在命令的末尾并且dup2成功,则perl在重定向之前写入Nul字节.这具有从命令中移除它的效果,例如,DNE 2>& 1变为DNE!现在,在命令中没有shell元字符的情况下,子进程中的perl认为自己是’Self,我们可以直接执行这个命令’.

对exec的调用失败,因为DNE不存在.孩子仍然在STDERR上发出失败的exec警告.由于dup2将STDERR指向与STDOUT相同的位置,因此它不会转到终端:管道的写入端返回到父节点.

父进程检测到子进程异常退出,并忽略管道内容,因为命令执行失败的结果记录为undef.

不同的警告

在这里,我们看到不存在DNE的不同诊断.遇到的第一个shell元字符是;,因此perl将命令不变地移交给shell执行.回声正常完成,然后DNE在shell中失败,shell的STDOUT和STDERR返回到父进程.从perl的角度来看,shell执行得很好,所以没有什么值得警告的.

相关说明

当您启用警告pragma-a Very Good Practice! – 这将启用exec警告类别.要查看这些警告的完整列表,请在perldiag文档中搜索字符串W exec.

观察差异.

后者的调用相当于

我喜欢在执行exec,管道打开等问题时格式化我自己的错误消息.这意味着我通常禁用exec警告,但这也意味着我必须非常小心地测试返回值. 总结

以上是内存溢出为你收集整理的linux – 如果反引号不能执行,则STDER的STDERR重定向会丢失全部内容,希望文章能够帮你解决linux – 如果反引号不能执行,则STDER的STDERR重定向会丢失所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/yw/1048088.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-25
下一篇 2022-05-25

发表评论

登录后才能评论

评论列表(0条)

保存