時々、エクスポートされたbash関数はPerlで見ることができます。

時々、エクスポートされたbash関数はPerlで見ることができます。

私の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

sh -cしたがって、Perlはコマンドが1つしかない場合は削除します。sh -cUbuntu 16.04の機能を活用してみましょうか?

/bin/shdash両方のシステムで。

編集2

envこの機能を見せてください。これは、両方のシステムで環境の一部である機能を示しています。

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

Ubuntu 16.04とオペレーティングシステム1つ:

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にエクスポートされた関数をエンコードするために使用される文字を含む、名前などの奇妙な文字を含む変数BASH_FUNC_foo%%=() { ...

|-fromopen関数の後に引数リストではなく単一の引数が続き、その引数にシェルメタ文字(バックスラッシュがその1つ)が含まれている場合、Perlはそれを直接使用するのではなく引数として渡し/bin/sh -cます。execvp(2)

Perlの、およびsystemのような機能にも同様に適用され、 に文書化されています。 execopen2perldoc -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}。これにより、すべてのシェル機能をシームレスに使用できます。

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

答え2

システム間でエクスポートされたbash機能の移植性に問題があります。はい、そうですね。

簡単に携帯性を向上させます。

$ touch myfunc
$ chmod a+x myfunc

#! /usr/bin/env bashそして、shebang行から始めて、必要なbashコードをファイルに入れます。 (env服従$PATHしてください。)

次に、バリアントがどのように振る舞うのか、それに準拠しているのではなく、shプログラム(gnu並列を含む)のフォーク+実行機能に依存します。dashbash

関連情報