My Redhat 9, OpenBSD 4.9, FreeBSD 10, Macos X, LinuxMint 17.3, and Ubuntu 14.04.4 all print OK when running this:
myfunc() { echo OK; }
export -f myfunc
perl -e open\(\$fh,\"\|-\",\"@ARGV\"\)\;close\$fh\; /bin/bash\ -c\ myfunc\\\ a
My Ubuntu 16.04.1 gives:
bash: myfunc: command not found
But if I remove \\\ a
it works.
perl -e open\(\$fh,\"\|-\",\"@ARGV\"\)\;close\$fh\; /bin/bash\ -c\ myfunc
I have the feeling something is configured wrongly on my system, but what should I look for?
Edit
thrig found a shorter version that also fails. Using that I straced on a failing and a non-failing system:
stdout strace -ff perl -e 'system @ARGV' /bin/bash\ -c\ myfunc\\\ a|grep bash
Failing:
execve("/usr/bin/perl", ["perl", "-e", "system @ARGV", "/bin/bash -c myfunc\\ a"], [/* 71 vars */]) = 0
[pid 7728] execve("/bin/sh", ["sh", "-c", "/bin/bash -c myfunc\\ a"], [/* 71 vars */]) = 0
[pid 7729] execve("/bin/bash", ["/bin/bash", "-c", "myfunc a"], [/* 70 vars */]) = 0
Non-failing:
execve("/usr/bin/perl", ["perl", "-e", "system @ARGV", "/bin/bash -c myfunc\\ a"], [/* 20 vars */]) = 0
[pid 26497] execve("/bin/sh", ["sh", "-c", "/bin/bash -c myfunc\\ a"], [/* 20 vars */]) = 0
[pid 26498] execve("/bin/bash", ["/bin/bash", "-c", "myfunc a"], [/* 20 vars */]) = 0
That looks awfully similar. Removing the \\\ a
gives on both systems:
execve("/usr/bin/perl", ["perl", "-e", "system @ARGV", "/bin/bash -c myfunc"], [/* 71 vars */]) = 0
[pid 7826] execve("/bin/bash", ["/bin/bash", "-c", "myfunc"], [/* 71 vars */]) = 0
So Perl drops the sh -c
if there is only a single command. Maybe sh -c
eats the function on the Ubuntu 16.04?
/bin/sh
is dash
on both systems.
Edit2
env
shows the function. This displays the function as part of the environment on both systems:
perl -e 'system @ARGV' /bin/bash\ -c\ env
Ubuntu 16.04 and one working system:
BASH_FUNC_myfunc%%=() { echo OK
}
Other working system:
BASH_FUNC_myfunc()=() { echo OK
}
But this shows only the definition on the working systems:
perl -e 'system @ARGV' /bin/bash\ -c\ env';true'
Edit3
Workaround:
myfunc() { echo OK; }
export -f myfunc
perl -e open\(\$fh,\"\|-\",@ARGV\)\;close\$fh\; /bin/bash -c myfunc\ a
Best Answer
The problem is that the
/bin/sh
from systems like Debian or Ubuntu (dash
) or OpenBSD will clear from the environment any variables whose names contain fancy chars like%
, and that includes thoseBASH_FUNC_foo%%=() { ...
, which are used to encode bash's exported functions.If the
|-
from theopen
function is followed by a single argument instead of a list of arguments, and that argument contains any shell metacharacters (the backslash is one of those), then perl will pass it as an argument to/bin/sh -c
instead of usingexecvp(2)
directly.The same holds true for the
system
,exec
,open2
, etc functions in perl, and is documented inperldoc -f system
.A simpler example:
The work-around is to run external commands with the user's
$ENV{SHELL}
, which will let them use any shell features seamlessly: