bash - script - Pase comandos como entrada a otro comando(su, ssh, sh, etc.)
shell linux ejemplos (3)
Tengo un script donde necesito iniciar un comando, luego pasar algunos comandos adicionales como comandos a ese comando. Lo intenté
su
echo I should be root now:
who am I
exit
echo done.
... pero no funciona: el
su
tiene éxito, pero luego el símbolo del sistema solo me está mirando.
Si
who am i
exit
en el indicador, el
echo
y
who am i
etc., comienzan a ejecutarse.
Y el
echo done.
no se ejecuta en absoluto.
Del mismo modo, necesito que esto funcione sobre
ssh
:
ssh remotehost
# this should run under my account on remotehost
su
## this should run as root on remotehost
whoami
exit
## back
exit
# back
¿Cómo puedo solucionar esto?
Estoy buscando respuestas que resuelvan esto de manera general, y que no sean específicas de
su
ossh
en particular. La intención es que esta pregunta se convierta en canonical para este patrón en particular.
Agregando a la answer :
Es importante recordar que la sección de la secuencia de comandos formateada como un documento aquí para otro shell se ejecuta en un shell diferente con su propio entorno (y tal vez incluso en una máquina diferente).
Si ese bloque de su secuencia de comandos contiene expansión de parámetros, sustitución de comandos y / o expansión aritmética, entonces debe usar la función de documento aquí del shell de forma ligeramente diferente, dependiendo de dónde desea que se realicen esas expansiones.
1. Todas las expansiones deben realizarse dentro del alcance del shell principal.
Entonces el delimitador del documento aquí debe estar sin comillas .
command <<DELIMITER
...
DELIMITER
Ejemplo:
#!/bin/bash
a=0
mylogin=$(whoami)
sudo sh <<END
a=1
mylogin=$(whoami)
echo a=$a
echo mylogin=$mylogin
END
echo a=$a
echo mylogin=$mylogin
Salida:
a=0
mylogin=leon
a=0
mylogin=leon
2. Todas las expansiones deben realizarse dentro del alcance del shell secundario.
Entonces se debe citar el delimitador del documento aquí.
command <<''DELIMITER''
...
DELIMITER
Ejemplo:
#!/bin/bash
a=0
mylogin=$(whoami)
sudo sh <<''END''
a=1
mylogin=$(whoami)
echo a=$a
echo mylogin=$mylogin
END
echo a=$a
echo mylogin=$mylogin
Salida:
a=1
mylogin=root
a=0
mylogin=leon
3. Algunas expansiones se deben realizar en el shell hijo, algunas, en el padre.
Entonces, el delimitador del documento aquí debe estar sin comillas y debe escapar de esas expresiones de expansión que deben realizarse en el shell secundario .
Ejemplo:
#!/bin/bash
a=0
mylogin=$(whoami)
sudo sh <<END
a=1
mylogin=/$(whoami)
echo a=$a
echo mylogin=/$mylogin
END
echo a=$a
echo mylogin=$mylogin
Salida:
a=0
mylogin=root
a=0
mylogin=leon
Un script de shell es una secuencia de comandos. El shell leerá el archivo de script y ejecutará esos comandos uno tras otro.
En el caso habitual, no hay sorpresas aquí; pero un error frecuente de principiante supone que algunos comandos tomarán el control del shell y comenzarán a ejecutar los siguientes comandos en el archivo de script en lugar del shell que actualmente ejecuta este script. Pero no es así como funciona.
Básicamente, las secuencias de comandos funcionan exactamente como los comandos interactivos, pero es necesario entender cómo funcionan exactamente . Interactivamente, el shell lee un comando (desde la entrada estándar), ejecuta ese comando (con la entrada desde la entrada estándar) y, cuando está listo, lee otro comando (desde la entrada estándar).
Ahora, al ejecutar un script,
la entrada estándar sigue siendo el terminal
(a menos que haya utilizado una redirección), pero los comandos se leen desde el archivo de script, no desde la entrada estándar.
(Lo contrario sería muy engorroso: cualquier
read
consumiría la siguiente línea del script,
cat
sorbería el resto del script, ¡y no habría forma de interactuar con él!) El archivo del script
solo
contiene comandos para instancia de shell que lo ejecuta (aunque, por supuesto, todavía puede usar un documento aquí, etc. para incrustar entradas como argumentos de comando).
En otras palabras, estos comandos "mal entendidos" (
su
,
ssh
,
sh
,
sudo
,
bash
, etc.) cuando se ejecutan solos (sin argumentos) iniciarán un shell interactivo, y en una sesión interactiva, obviamente está bien;
pero cuando se ejecuta desde un script, muy a menudo no es lo que quieres.
Todos estos comandos tienen formas de aceptar comandos de otras maneras que no sean en una sesión de terminal interactiva. Por lo general, cada comando admite una forma de pasar los comandos como opciones o argumentos:
su root -c ''who am i''
ssh user@remote uname -a
sh -c ''who am i; echo success''
Muchos de estos comandos también aceptarán comandos en la entrada estándar:
printf ''uname -a; who am i; uptime'' | su
printf ''uname -a; who am i; uptime'' | ssh user@remote
printf ''uname -a; who am i; uptime'' | sh
que también te permite usar convenientemente documentos aquí:
ssh user@remote <<''____HERE''
uname -a
who am i
uptime
____HERE
sh <<''____HERE''
uname -a
who am i
uptime
____HERE
Para los comandos que aceptan un solo argumento de comando, ese comando puede ser
sh
o
bash
con múltiples comandos:
sudo sh -c ''uname -a; who am i; uptime''
Por otro lado, generalmente no necesita una
exit
explícita porque el comando terminará de todos modos cuando haya ejecutado el script (secuencia de comandos) que pasó para su ejecución.
Si desea una solución genérica que funcione para cualquier tipo de programa, puede usar el comando
expect
.
Extracto de la página del manual:
Expect
es un programa que "habla" con otros programas interactivos de acuerdo con un guión. Siguiendo el guión,Expect
sabe qué se puede esperar de un programa y cuál debería ser la respuesta correcta. Un lenguaje interpretado proporciona ramificaciones y estructuras de control de alto nivel para dirigir el diálogo. Además, el usuario puede tomar el control e interactuar directamente cuando lo desee, y luego devolver el control al script.
Aquí hay un ejemplo de trabajo que usa
expect
:
set timeout 60
spawn sudo su -
expect "*?assword" { send "*secretpassword*/r" }
send_user "I should be root now:"
expect "#" { send "whoami/r" }
expect "#" { send "exit/r" }
send_user "Done./n"
exit
El script se puede iniciar con un comando simple:
$ expect -f custom.script
Puede ver un ejemplo completo en la siguiente página: http://www.journaldev.com/1405/expect-script-example-for-ssh-and-su-login-and-running-commands
Nota: La respuesta propuesta por @tripleee solo funcionaría si la entrada estándar pudiera leerse una vez al inicio del comando, o si se hubiera asignado un tty, y no funcionará para ningún programa interactivo.
Ejemplo de errores si usa una tubería
echo "su whoami" |ssh remotehost
--> su: must be run from a terminal
echo "sudo whoami" |ssh remotehost
--> sudo: no tty present and no askpass program specified
En SSH, puede forzar una asignación TTY con múltiples parámetros
-t
, pero cuando
sudo
le pedirá la contraseña, fallará.
Sin el uso de un programa como
expect
cualquier llamada a una función / programa que pueda obtener información de stdin hará que falle el siguiente comando:
ssh use@host <<''____HERE''
echo "Enter your name:"
read name
echo "ok."
____HERE
--> The `echo "ok."` string will be passed to the "read" command