Warum verlässt „bash -c somecommand“ manchmal einen Bash-Prozess nicht?

Warum verlässt „bash -c somecommand“ manchmal einen Bash-Prozess nicht?

Auf einem Ubuntu 12.04 mit GNU Bash, Version 4.2.25(1)-Release (x86_64-pc-linux-gnu), habe ich den folgenden Befehl ausprobiert:

$ bash -c 'pstree -s $$'
init───sshd───sshd───sshd───bash───pstree
$ bash -c 'pstree -s $$;echo'
init───sshd───sshd───sshd───bash───bash───pstree

Ich denke, das Zweite entspricht meinen Erwartungen: Im ersten bashhabe ich diese Befehle ausgeführt, im zweiten bashhabe ich begonnen bash -c ..., und dann startet die zweite Bash einen Unterprozess namens pstree.

Ich frage mich jedoch, was mit dem ersten passiert ist. Warum ist der zweite bashverschwunden und pstreewurde zu einem Unterprozess des Originals bash? Und warum gilt die Antwort auf die vorherige Frage nicht für den zweiten bash -c ...?

Antwort1

Ich hätte gesagt, es ist einfachSchwanz Anruf Optimierung, aber tatsächlich (wie der letzte Link zeigt) bashoptimiert es keine Tail-Calls. Es scheint nur den Fall zu optimieren, in dem der auszuführende Befehl ein einfacher Befehl ist (also kein zusammengesetzter Befehl), jedoch nicht, wenn Umleitungen vorhanden sind. Es scheint auch zu optimieren, wenn der Befehl der letzte in einer Und-oder-Liste ist (wie in true && true && cmdoder false && true || cmd), jedoch nicht, wenn traps vorhanden sind (wie in trap uname EXIT && cmd), da dies falsch wäre.

Der zweite Befehl ist kein Tail-Call von pstree, da pstreenicht das letzte Element in der Befehlszeile ist. (Es ist ein Tail-Call von echo, echoist aber normalerweise integriert, sodass trotzdem kein Unterprozess dafür erstellt wird.)

Um das Lesen all dieser Links zu sparen (obwohl ich hoffe, dass sie interessant sind), ist die Idee, dass, wenn Sie wissen, dass eine Funktion/ein Programm/was auch immer unmittelbar nach dem Aufruf einer anderen Funktion/eines anderen Programms/was auch immer zurückkehrt und dass der zurückgegebene Wert der von der aufgerufenen Funktion/dem aufgerufenen Programm/was auch immer zurückgegebene Wert ist, Sie genauso gut den aktuellen Stack-Frame (oder Prozess, im Fall eines Shell-Skripts) wiederverwenden können, anstatt einen neuen Stack-Frame zu pushen (einen neuen Prozess zu erstellen), die Funktion aufzurufen (das Skript auszuführen) und dann zurückzukehren. In einem Shell-Skript können Sie das manuell tun, indem Sie execfür den letzten Befehl verwenden, aber es wäre möglich, dass die Shell das automatisch tut.

zshund kshbeide scheinen dazu in der Lage zu sein, aber nicht 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

Aber diese Beobachtung ist nur eine Beobachtung, die auf den Versionen dieser Shells basiert, die ich zufällig installiert habe, also kann Ihre Situation unterschiedlich sein:

$ 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)

verwandte Informationen