`bash -c somecommand` が bash プロセスを終了しないことがあるのはなぜですか?

`bash -c somecommand` が bash プロセスを終了しないことがあるのはなぜですか?

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されないようです。最適化するのは誤りだからです。traptrap uname EXIT && cmd

2 番目のコマンドは、がコマンド ラインの最後ではないpstreeため、の末尾呼び出しではありません。( の末尾呼び出しですが、 は通常は組み込みであるため、それに対してサブプロセスは作成されません。)pstreeechoecho

これらすべてのリンクを読む手間を省くために (興味深いものであることを願いますが)、関数/プログラム/その他が他の関数/プログラム/その他を呼び出した直後に戻り、返される値が呼び出された関数/プログラム/その他によって返される値になることがわかっている場合は、新しいスタック フレームをプッシュ (新しいプロセスを作成) し、関数を呼び出し (スクリプトを実行) してから戻るのではなく、現在のスタック フレーム (シェル スクリプトの場合はプロセス) を再利用する方がよいという考え方です。シェル スクリプトでは、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)

関連情報