Escribí un script que cambia a los usuarios mientras se ejecuta, y lo ejecuté usando la redirección de archivos al estándar. Así user-switch.sh
es ...
#!/bin/bash
whoami
sudo su -l root
whoami
Y ejecutarlo bash
me da el comportamiento que espero
$ bash < user-switch.sh
vagrant
root
Sin embargo, si ejecuto el script con sh
, obtengo un resultado diferente
$ sh < user-switch.sh
vagrant
vagrant
¿Por qué está bash < user-switch.sh
dando resultados diferentes que sh < user-switch.sh
?
Notas:
- sucede en dos cajas diferentes que ejecutan Debian Jessie
bash
io-redirection
stdin
dash
popedotninja
fuente
fuente
sudo su
: unix.stackexchange.com/questions/218169/…/bin/sh
en (mi copia de) Debian es Dash, sí. Ni siquiera sabía qué era eso hasta ahora. Me he quedado con bash hasta ahora.user-switch.sh
en un trabajo de Jenkins, y se ejecutó el script. Ejecutar el mismo script fuera de Jenkins produjo resultados diferentes, y recurrí a la redirección de archivos para obtener el comportamiento que vi en Jenkins.Respuestas:
Un guión similar, sin
sudo
resultados, pero similares:Con
bash
, el resto del script va como entrada parased
, condash
, el shell lo interpreta.Corriendo
strace
en esos:dash
lee un bloque del script (ocho kB aquí, más que suficiente para contener todo el script), y luego generased
:Lo que significa que el controlador de archivo está al final del archivo y
sed
no verá ninguna entrada. La parte restante está almacenada en el interiordash
. (Si el script fuera más largo que el tamaño de bloque de 8 kB, la parte restante sería leída porsed
).Bash, por otro lado, busca el final del último comando:
Si la entrada proviene de una tubería, como aquí:
no se puede rebobinar, ya que no se pueden buscar tuberías y enchufes. En este caso, Bash recurre a la lectura de la entrada de un carácter a la vez para evitar una sobre lectura. (
fd_to_buffered_stream()
ininput.c
) Hacer una llamada de sistema completo para cada byte no es muy efectivo en principio. En la práctica, no creo que las lecturas sean una gran sobrecarga en comparación, por ejemplo, con el hecho de que la mayoría de las cosas que hace el shell implican generar procesos completamente nuevos.Una situación similar es esta:
El subshell debe asegurarse de que
read
solo lea la primera línea nueva, para quehead
vea la siguiente línea. (Esto funcionadash
también con ).En otras palabras, Bash hace todo lo posible para admitir la lectura de la misma fuente para el script en sí y para los comandos ejecutados desde él.
dash
no lo hace Elzsh
, yksh93
empaquetado en Debian van con Bash en esto.fuente
strace
más tarde y compararé las salidas. Sin embargo, no había usado ese enfoque.El shell está leyendo el script desde la entrada estándar. Dentro del script, ejecuta un comando que también quiere leer la entrada estándar. ¿Qué entrada va a ir a dónde? No se puede decir de manera confiable .
La forma en que funcionan los shells es que leen un fragmento de código fuente, lo analizan y, si encuentran un comando completo, ejecutan el comando y luego continúan con el resto del fragmento y el resto del archivo. Si el fragmento no contiene un comando completo (con un carácter final al final, creo que todos los shells se leen hasta el final de una línea), el shell lee otro fragmento, y así sucesivamente.
Si un comando en el script intenta leer desde el mismo descriptor de archivo del que el shell está leyendo el script, entonces el comando encontrará lo que venga después del último fragmento que leyó. Esta ubicación es impredecible: depende del tamaño de fragmento que seleccionó el shell, y eso puede depender no solo del shell y su versión, sino de la configuración de la máquina, la memoria disponible, etc.
Bash busca el final del código fuente de un comando en el script antes de ejecutar el comando. Esto no es algo con lo que pueda contar, no solo porque otros shells no lo hacen, sino también porque esto solo funciona si el shell está leyendo desde un archivo normal. Si el shell está leyendo desde una tubería (por ejemplo
ssh remote-host.example.com <local-script-file.sh
), los datos que se han leído se leen y no se pueden leer.Si desea pasar la entrada a un comando en el script, debe hacerlo explícitamente, generalmente con un documento aquí . (Un documento aquí suele ser el más conveniente para la entrada de varias líneas, pero cualquier método funcionará). El código que escribió solo funciona en unos pocos shells, solo si el script se pasa como entrada al shell desde un archivo normal; si esperaba que el segundo
whoami
se pasara como entradasudo …
, piense de nuevo, teniendo en cuenta que la mayoría de las veces el script no se pasa a la entrada estándar del shell.Tenga en cuenta que esta década, puede usar
sudo -i root
. Corrersudo su
es un truco del pasado.fuente