※本記事の内容はWindowsには一切当てはまりません。
※Linux (Debian stretch) / macOS (10.14) で確認。
https://linuxjm.osdn.jp/html/LDP_man-pages/man3/exec.3.html 等によると、execvpでのみ、ENOEXECが発生したらシェルスクリプトと解釈する機能があるそうです。
スクリプト言語でシェルを使わず外部プログラムを実行した場合、この機能は働くのでしょうか?
準備
$ cat lsscript
ls
実行
Ruby
- 直接実行(例): popenの引数が配列、systemの引数が2要素以上
ruby -e 'p IO.popen(["./lsscript"]).read'
process.c proc_exec_cmd
execveを試し、ENOEXECなら/bin/shを付加して再試行
Python
- 直接実行(例): check_call(等)の引数が配列
python -c 'import subprocess;subprocess.check_call(["./lsscript"])'
Modules/_posixsubprocess.c child_exec
execveを試す。 ENOEXECの場合失敗する
Python3も同様。
なおshell=Trueとすると失敗しなくなりますがpipes.quoteやらshlex.quoteやらが必要となります。
(この記事の読者であればこちらの必要性はわかりますよね)
Perl
- 直接実行(例): system()の引数が2要素以上
perl -e 'print system("./lsscript","dummy")'
doio.c Perl_do_aexec5
execvpを試す。
PHP
- 直接実行: pcntl_exec
php -r 'pcntl_exec("./lsscript");'
ext/pcntl/pcntl.c pcntl_exec
execveを試す。 ENOEXECの場合失敗する
(上はexecですからサブプロセスとするにはfork呼び出し等が必要。その辺が必要ない標準実行系はext/standard/exec.cにありますが、popen固定のようです(つまり必ずシェルが入る))
結論
Python/PHPでシェルを介さずに実行しようとすると、自動でshを付けてくれません。C言語のshebangもどき等を投げる際はご注意ください。