有时从 Perl 中可以看到导出的 bash 函数

有时从 Perl 中可以看到导出的 bash 函数

运行以下命令时,我的 Redhat 9、OpenBSD 4.9、FreeBSD 10、Macos X、LinuxMint 17.3 和 Ubuntu 14.04.4 都打印 OK:

myfunc() { echo OK; }
export -f myfunc
perl -e open\(\$fh,\"\|-\",\"@ARGV\"\)\;close\$fh\; /bin/bash\ -c\ myfunc\\\ a

我的 Ubuntu 16.04.1 给出:

bash: myfunc: command not found

但如果我删除\\\ a它就可以了。

perl -e open\(\$fh,\"\|-\",\"@ARGV\"\)\;close\$fh\; /bin/bash\ -c\ myfunc

我感觉我的系统上有些配置错误,但我应该寻找什么?

编辑

thrig 找到了一个较短的版本,但也失败了。使用它我追踪了一个失败的和一个非失败的系统:

stdout strace -ff perl -e 'system @ARGV' /bin/bash\ -c\ myfunc\\\ a|grep bash

失败:

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

无失败:

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

看起来非常相似。删除\\\ a两个系统上的给定:

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

所以 Perl 会删除sh -cif 只有一个命令。也许sh -c吃Ubuntu 16.04上的功能?

/bin/shdash两个系统上。

编辑2

env显示该功能。这将函数显示为两个系统上环境的一部分:

perl -e 'system @ARGV' /bin/bash\ -c\ env

Ubuntu 16.04 和一个操作系统:

BASH_FUNC_myfunc%%=() {  echo OK
}

其他工作制度:

BASH_FUNC_myfunc()=() {  echo OK
}

但这仅显示了工作系统上的定义:

perl -e 'system @ARGV' /bin/bash\ -c\ env';true'

编辑3

解决方法:

myfunc() { echo OK; }
export -f myfunc
perl -e open\(\$fh,\"\|-\",@ARGV\)\;close\$fh\; /bin/bash -c myfunc\ a

答案1

问题是/bin/shDebian 或 Ubuntu ( dash) 或 OpenBSD 等系统将从环境中清除名称包含诸如 、 之类的奇特字符的任何变量%,其中包括那些BASH_FUNC_foo%%=() { ...用于对 bash 导出函数进行编码的变量。

如果|-fromopen函数后跟单个参数而不是参数列表,并且该参数包含任何 shell 元字符(反斜杠就是其中之一),那么 perl 会将其作为参数传递给/bin/sh -c而不是execvp(2)直接使用。

这同样适用于perl 中的systemexecopen2等函数,并在 中进行了记录perldoc -f system

一个更简单的例子:

$ foo(){ echo foo; }; export -f foo

$ perl -e 'system shift' '/bin/bash -c foo\ a'
/bin/bash: foo: command not found
$ perl -e 'system shift' '/bin/bash -c foo'
foo
$ perl -e 'system shift' '/bin/bash -c "foo"'
/bin/bash: foo: command not found

$ perl -e 'open F, "|-", shift' '/bin/bash -c foo\ a'
/bin/bash: foo: command not found
$ perl -e 'open F, "|-", shift' '/bin/bash -c foo'
foo

解决方法是使用用户的 运行外部命令$ENV{SHELL},这将使他们能够无缝地使用任何 shell 功能:

perl -e 'open F, "|-", $ENV{SHELL}, "-c", "@ARGV"' '/bin/bash -c "foo a"'

答案2

您在跨系统导出的 bash 函数的可移植性方面遇到了问题。是的,这是有道理的。

简单地提高便携性

$ touch myfunc
$ chmod a+x myfunc

并将所需的 bash 代码放入该文件中,以#! /usr/bin/env bashshebang 行开头。 (env服从$PATH,你瞧。)

然后,您将依赖于程序(包括 gnu 并行)进行 fork+exec 的能力,而不是依赖于变体对行为的喜欢shdash符合程度bash

相关内容