My goal is to capture the exit code of external command issued from perl. The trick is that the external command is actually composed of two commands where first command is piped to second command. For my purposes I need exit code of first command. Bash uses for this purpose several possible ways e.g.
$ false | true; echo "${PIPESTATUS[0]}"
1
$ true | false; echo "${PIPESTATUS[0]}"
0
After reading about this problematic I've found that perl system
command is able to get exit code of process so I've tried following:
$ exit 10 | true
$ echo ${PIPESTATUS[0]}
10
$ true | false
$ echo $?
1
$ perl -e 'my $code = system("true | false"); print $code . "\n"'
256
$ perl -e 'my $code = system("bash -c \"true | false\""); print $code . "\n"'
256
$ perl -e 'my $code = system("bash -c \"exit 2\""); print $code . "\n"'
512
$ perl -e 'my $code = system("bash 2>&1 >/dev/null"); print $code . "\n"'
$ exit 10
exit
2560
As you can see I am getting weird return codes (256, 512, 2560) from system
command. I think it is related to my another question. So far the only possible way that I am able to access return code of first command is using qw
or backtickss ``. This seems as a a little overkill for me since I need one echo
to capture the PIPESTATUS[0]
value.
$ perl -e 'my $res = qx/true | false 2>&1 >\/dev\/null; echo \${PIPESTATUS[0]}/; print $res'
0
$ perl -e 'my $res = qx/false | true 2>&1 >\/dev\/null; echo \${PIPESTATUS[0]}/; print $res'
1
$ perl -e 'my $res = qx"false | true 2>&1 >/dev/null; echo \${PIPESTATUS[0]}"; print $res'
1
$ perl -e 'my $res = qx"true | false 2>&1 >/dev/null; echo \${PIPESTATUS[0]}"; print $res'
0
$ perl -e 'my $res = qx(false | true 2>&1 >/dev/null; echo \${PIPESTATUS[0]}); print $res'
1
$ perl -e 'my $res = `false | true 2>&1 >/dev/null; echo \${PIPESTATUS[0]}`; print $res'
1
I am also wondering why this approach is even working because here is mentioned that perl does not invoke a shell to execute external command. So where does PIPESTATUS
(which is a pure bash variable) comes from? Also I would expect that following command will work since bash is explicitly issued, but it returns nothing:
$ perl -e 'my $res = `bash -c "false | true 2>&1 >/dev/null; echo \${PIPESTATUS[0]}"`; print $res'
$
The third miss understanding is based on this answer that I can directly access PIPESTATUS
variable via assigning it to variable and then access as a regular perl variable e.g.
status=(${PIPESTATUS[@]})
print $status
However the following command does not work for me.
$ perl -e 'my $res = `false | true 2>&1 >/dev/null;`; status=(${PIPESTATUS[@]})'
syntax error at -e line 1, near "@]}"
Missing right curly or square bracket at -e line 1, at end of line
Execution of -e aborted due to compilation errors.
@EDIT in reply to ThisSuitIsBlackNot, tjd and CapEnt
Return codes and bit shifting explained:
$ perl -e 'my $code = system("true | false"); print $code >> 8; print "\n"'
1
$ perl -e 'my $code = system("bash -c \"true | false\""); print $code >> 8; print "\n"'
1
$ perl -e 'my $code = system("bash -c \"exit 2\""); print $code >> 8; print "\n"'
2
Some systems have /bin/sh
pointed to bash
...
# Linux arch 3.18.6-1-ARCH #1 SMP PREEMPT Sat Feb 7 08:59:29 CET 2015 i686 GNU/Linux
$ ls -la `which sh`
lrwxrwxrwx 1 root root 4 Dec 30 23:11 /usr/bin/sh -> bash
$ perl -e 'my $cmd = "exit 255 | true 2>&1 >/dev/null; echo \${PIPESTATUS[0]}"; system($cmd);'
255
some not ...
# SunOS andromeda 5.9 Generic_122300-61 sun4u sparc SUNW,Sun-Fire-V490
$ ls -la `which sh`
-r-xr-xr-x 4 root root 95504 Jul 16 2009 /bin/sh
$ perl -e 'my $cmd = "exit 255 | true 2>&1 >/dev/null; echo \${PIPESTATUS[0]}"; system($cmd);'
sh: bad substitution
a little hack that will use bash
regardless of what /bin/sh
is pointing to:
# Linux arch 3.18.6-1-ARCH #1 SMP PREEMPT Sat Feb 7 08:59:29 CET 2015 i686 GNU/Linux
# SunOS andromeda 5.9 Generic_122300-61 sun4u sparc SUNW,Sun-Fire-V490
$ perl -e 'my $cmd = "bash -c \"exit 255 | true 2>&1 >/dev/null; echo \\\${PIPESTATUS[0]}\""; system($cmd);'
255
Here you can see that bash is invoked in both cases (in 2nd case bash
is children of sh
)
# Linux arch 3.18.6-1-ARCH #1 SMP PREEMPT Sat Feb 7 08:59:29 CET 2015 i686 GNU/Linux
$ perl -e 'my $code = system("bash -c \"ps -elf | grep $$\"");'
0 S wakatana 1576 282 0 80 0 - 1606 wait 16:46 pts/1 00:00:00 perl -e my $code = system("bash -c \"ps -elf | grep $$\"");
0 S wakatana 1577 1576 0 80 0 - 1333 wait 16:46 pts/1 00:00:00 bash -c ps -elf | grep 1576
0 S wakatana 1579 1577 0 80 0 - 1167 pipe_w 16:46 pts/1 00:00:00 grep 1576
# SunOS andromeda 5.9 Generic_122300-61 sun4u sparc SUNW,Sun-Fire-V490
$ perl -e 'my $code = system("bash -c \"ps -elf | grep $$\"");'
8 S wakatana 24641 24640 0 60 20 ? 314 ? 16:01:45 pts/196 0:00 bash -c ps -elf | grep 24639
8 S wakatana 24640 24639 0 50 20 ? 139 ? 16:01:45 pts/196 0:00 sh -c bash -c "ps -elf | grep 24639
8 S wakatana 24643 24641 0 50 20 ? 128 ? 16:01:45 pts/196 0:00 grep 24639
8 S wakatana 24639 24633 0 50 20 ? 383 ? 16:01:45 pts/196 0:00 perl -e my $code = system("bash -c
As ThisSuitIsBlackNot pointed out, probably the best solution is to use IPC::Run but I am wondering if this is OK in case when I need pure perl (without modules)