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 bash
es donde ejecuté estos comandos; el segundo bash
es 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 bash
desapareció y se pstree
convirtió 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), bash
no 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 && cmd
o false && true || cmd
) pero no cuando hay trap
s 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 pstree
no es lo último en la línea de comando. (Es una llamada final de echo
, pero echo
generalmente 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 exec
el último comando, pero sería posible que el shell lo hiciera automáticamente.
zsh
y ksh
ambos 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)