Ubuntu 12.04、GNU bash バージョン 4.2.25(1) リリース (x86_64-pc-linux-gnu) で、次のコマンドを試しました。
$ bash -c 'pstree -s $$'
init───sshd───sshd───sshd───bash───pstree
$ bash -c 'pstree -s $$;echo'
init───sshd───sshd───sshd───bash───bash───pstree
2 番目は私の予想通りだと思います。1 番目はbash
これらのコマンドを実行した場所、2 番目はbash
で開始した場所bash -c ...
、そして 2 番目の bash は と呼ばれるサブプロセスを開始しますpstree
。
しかし、最初のものに何が起こったのか疑問に思います。なぜ 2 番目がbash
消えて、 がpstree
元の のサブプロセスになったのでしょうかbash
? また、前の質問の答えが 2 番目には当てはまらないのはなぜでしょうかbash -c ...
?
答え1
それは単純にしっぽ 電話 最適化ですが、実際は (最後のリンクが指摘しているように)bash
末尾呼び出しは最適化されません。実行されるコマンドが単純なコマンド (つまり、複合コマンドではない) の場合のみ最適化されるようですが、リダイレクトがある場合は最適化されません。また、コマンドが and-or リストの最後 (または の場合) にある場合は最適化されるようですtrue && true && cmd
が、がある場合 ( の場合) は最適化false && true || cmd
されないようです。最適化するのは誤りだからです。trap
trap uname EXIT && cmd
2 番目のコマンドは、がコマンド ラインの最後ではないpstree
ため、の末尾呼び出しではありません。( の末尾呼び出しですが、 は通常は組み込みであるため、それに対してサブプロセスは作成されません。)pstree
echo
echo
これらすべてのリンクを読む手間を省くために (興味深いものであることを願いますが)、関数/プログラム/その他が他の関数/プログラム/その他を呼び出した直後に戻り、返される値が呼び出された関数/プログラム/その他によって返される値になることがわかっている場合は、新しいスタック フレームをプッシュ (新しいプロセスを作成) し、関数を呼び出し (スクリプトを実行) してから戻るのではなく、現在のスタック フレーム (シェル スクリプトの場合はプロセス) を再利用する方がよいという考え方です。シェル スクリプトでは、exec
最後のコマンドに を使用して手動でこれを行うことができますが、シェルが自動的に行うことも可能です。
zsh
どちらもksh
それができるようですが、次のことはできませんbash
:
$ zsh -c 'if [[ -n foo ]]; then pstree -s $$; else echo hello; fi'
init───lightdm───lightdm───init───konsole───bash───pstree
$ ksh -c 'if [[ -n foo ]]; then pstree -s $$; else echo hello; fi'
init───lightdm───lightdm───init───konsole───bash───pstree
$ bash -c 'if [[ -n foo ]]; then pstree -s $$; else echo hello; fi'
init───lightdm───lightdm───init───konsole───bash───bash───pstree
しかし、この観察は、私がたまたまインストールしたシェルのバージョンに基づいた単なる観察なので、結果は人によって異なります。
$ zsh --version
zsh 5.0.2 (x86_64-pc-linux-gnu)
$ ksh --version
version sh (AT&T Research) 93u+ 2012-08-01
$ bash --version
GNU bash, version 4.2.45(1)-release (x86_64-pc-linux-gnu)