¿Por qué a veces `bash -c somecommand` no abandona un proceso bash?

¿Por qué a veces `bash -c somecommand` no abandona un proceso bash?

En Ubuntu 12.04, con GNU bash, versión 4.2.25(1) (x86_64-pc-linux-gnu), probé el siguiente comando:

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

Creo que el segundo cumple con mis expectativas: el primero bashes donde ejecuté estos comandos; el segundo bashes con el que comencé bash -c ...; y luego el segundo bash iniciará un subproceso llamado pstree.

Sin embargo, me pregunto qué pasó con el primero. ¿Por qué el segundo bashdesapareció y se pstreeconvirtió en un subproceso del original bash? ¿Y por qué la respuesta a la pregunta anterior no se aplica a la segunda bash -c ...?

Respuesta1

Yo hubiera dicho que es simplementeCola Llamar Mejoramiento, pero de hecho (como señala el último enlace), bashno optimiza las llamadas finales. Sólo parece optimizar el caso en el que el comando a ejecutar es un comando simple (es decir, no un comando compuesto), aunque no cuando hay redirecciones. También parece optimizarse cuando el comando es el último en una lista y/o (como en true && true && cmdo false && true || cmd) pero no cuando hay traps en su lugar (como en trap uname EXIT && cmd), ya que sería incorrecto hacerlo.

El segundo comando no es una llamada final de pstree, porque pstreeno es lo último en la línea de comando. (Es una llamada final de echo, pero echogeneralmente está integrada, por lo que no se creará ningún subproceso de todos modos).

Para ahorrarnos la lectura de todos esos enlaces (aunque espero que sean interesantes), la idea es que si sabes que una función/programa/lo que sea volverá inmediatamente después de llamar a alguna otra función/programa/lo que sea, y que el valor devuelto será el valor devuelto por la función/programa/lo que sea llamado, entonces también podría reutilizar el marco de pila actual (o proceso, en el caso de un script de shell) en lugar de impulsar un nuevo marco de pila (creando un nuevo proceso), llamando al función (ejecutar el script) y luego regresar. En un script de shell, puede hacerlo manualmente usando execel último comando, pero sería posible que el shell lo hiciera automáticamente.

zshy kshambos parecen poder hacer eso, pero no 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

Pero esa observación es sólo una observación, basada en las versiones de esos shells que tengo instalados, así que YMMV:

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

información relacionada