Redirecting STDERR to STDOUT lost if callbacks fail

I find that redirection STDERRwhen calling backticks may be lost if the command fails. I am shocked by the behavior that I see.

$ 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 '
123
sh: DNE: command not found

Is my syntax wrong?

I am using Perl 5.8.5 for Linux.

+5
source share
3 answers

, perl .

, , , .

my $foopath = "/usr/bin/foo";
die "$0: $foopath is not executable" unless -x $foopath;

# later ...

my $output = `$foopath 2>&1`;
die "$0: $foopath exited $?" if $?;

, Unix. .

Unix

perl.

perl -e 'print "hi\n"; warn "bye\n"'

hi
bye

, print STDOUT, warn STDERR . , .

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

/dev/null , "" .

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

1 STDOUT. "",

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

, 2 STDERR. ( STDIN 0.)

STDOUT STDERR 2>&1. " 2s , 1s".

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

, , . ,

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

, ,

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

. 2>&1, STDERR , STDOUT, : ! >/dev/null STDOUT .

, dup2, fcntl.

, .

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

, , STDERR STDOUT - . , "" . , STDOUT.

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

, fork, dup2 exec .

  • shell: fork sed
  • shell: sed waitpid
    • sed: pipe perl
    • sed: fork perl
    • sed: dup2, STDIN
    • sed: exec sed
    • sed: STDIN
      • perl: dup2 STDOUT 3
      • perl: dup2 STDERR STDOUT s
      • perl: exec perl
      • perl: exit
    • sed:
    • sed:
    • sed: reap perl s waitpid
    • sed: exit
  • shell: $? waitpid

, . , Bourne .

" "

Perl .

#! /usr/bin/env perl

use strict;
use warnings;

my $pid = open my $fh, "-|";
die "$0: fork: $!" unless defined $pid;

if ($pid) {
  while (<$fh>) {
    s/^/got: /;
    print;
  }
}
else {
  open STDERR, ">&=", \*STDOUT or print "$0: dup: $!";
  exec "perl", "-e", q[print "hi\n"; warn "bye\n"]
    or die "$0: exec: $!";
}

open , perlfunc open:

, MODE "|-", , , MODE "-|", , , ( ) tash ("-") . . open IPC perlipc .

$ ./simple-pipeline
got: bye
got: hi

hardcodes STDOUT, .

$ ./simple-pipeline >/dev/null

backticks

, perl , pp_backtick ( pp_sys.c), Perl_my_popen ( util.c) (fork, pipe, dup2). Perl_do_exec3 ( doio.c), , . :

/* handle the 2>&1 construct at the end */

2>&1, STDOUT , .

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] = '\0';
        break;
    }
}

PerlProc_execl(PL_sh_path, "sh", "-c", cmd, (char *)NULL);
PERL_FPU_POST_EXEC
S_exec_failed(aTHX_ PL_sh_path, fd, do_report);

S_exec_failed

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

, .

, 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.

.

. , 2>&1 , , :

if (*s != ' ' && !isALPHA(*s) &&
    strchr("$&*(){}[]'\";\\|?<>~`\n",*s)) {

. backticks , perl . , perl exec , fork .

DNE , perl . exec , , warnings. perlop , backticks qx// undef , , undefined $out.

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

exec?

, :

  • .
  • fork .
  • dup2 STDOUT .
  • exec, .
  • .

, perl . DNE 2>&1, perl STDERR STDOUT, .

if (!*t && (PerlLIO_dup2(1,2) != -1)) {
    s[-2] = '\0';
    break;
}

2>&1 , dup2 - , perl . , , DNE 2>&1 DNE! , , perl : "", exec .

exec , DNE . exec STDERR. - dup2, STDERR , STDOUT: .

, , undef.

$ perl -e 'use strict; use warnings; my $out=`echo 123; DNE 2>&1`; print $out'
123
sh: DNE: command not found

DNE, . ;, perl . echo , DNE , STDOUT STDERR . perl s , .

warnings -a !, exec. , perldiag W exec.

.

$ 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.

use strict;
use warnings;
no warnings 'exec';

my $out = `DNE`;
print defined($out) ? $out : "command failed\n";

, - exec, pipe open . , exec, , , .

+14

, . `...` . Perl DNE- $PATH, .

stdout, stderr, , , , IPC:: Open3, IPC:: Run.

, , , , , :

$ perl -e 'use strict; use warnings; my $o=`sh -c "DNE 2>&1"`; print $o' 
sh: 1: DNE: not found
+7
  my $op =`dne 2>&1;`;

It works. Note the semicolon ;at the end of the redirect.

Or you can use the code below.

#!/usr/bin/perl
use strict;
use warnings;

my $op=`dne 2>&1 1>output.txt`;

print $op;

Conclusion:

sh: dne: command not found

Although, why exactly STDOUT is not printed in the case dne 2>&1, I still do not know.

But when you use STDOUT redirection to a file, the output is printed. This is strange, but it works.

+1
source

All Articles