Tengo un script local que quiero ejecutar en varios servidores remotos. La sintaxis del comando que estoy usando es:
ssh <remote_server> "bash -s" < ./local_script.sh
Esto funciona bien e incluso me permite pasar parámetros alocal_script.sh. Sin embargo, realmente me gustaría pasarle un archivo de entrada, como en:
local_script.sh < local_file.txt
Combinando estas dos declaraciones se obtiene:
ssh <remote_server> "bash -s" < ./local_script.sh < local_file.txt
Cuando ejecuto esto, obtengo
bash: line 1: FOO: command not found
bash: line 2: BAR: command not found
bash: line 3: BAZ: command not found
...
donde FOO, BAR, BAZ, etc. son la primera palabra en cada línea dearchivo_local.txt:
FOO FOO_PARAM1
BAR BAR_PARAM1
BAZ BAZ_PARAM2
...
Entonces, parece que "bash -s"
en el servidor remoto se está interpretando.archivo_local.txtcomo un archivo de script, en lugar de un archivo de entrada paralocal_script.sh. ¿Hay alguna forma de solucionar este problema (aparte de crear un script contenedor)?
Respuesta1
Si bien ssh
proporciona dos flujos de salida separados (para stdout y stderr), solo alimenta un flujo de entrada (stdin). Por lo tanto, necesitaría pasar el contenido del script y el archivo de entrada a través de diferentes mecanismos.
Por ejemplo, uno mediante una variable, otro mediante stdin:
LC_CODE=$(cat local_script.sh) ssh host 'eval "$LC_CODE"' < input
(suponiendo que su ssh
cliente pasa la LC_*
variable ( SendEnv
in ssh_config
) y el sshd
servidor las acepta ( AcceptEnv
in sshd_config
))
O simplemente pase el contenido de local_script.sh
como código de shell remoto, asumiendo que el shell de inicio de sesión del usuario remoto es el correcto para la sintaxis de ese script:
ssh host "$(cat local_script.sh)" < input
O concatenar el código y la entrada enviados a ssh
la entrada estándar como con:
{
echo '{'; cat local_script.sh; echo '}; exit'
cat input
} | ssh host 'bash -s some arguments'
Aquí, el uso de bash
porque bash
se encargará de leer la entrada un byte a la vez para no leer más allá de esa }; exit
línea mientras que no todos los demás shells lo hacen.
O si sed
en el host remoto está GNU sed
:
echo '#END-OF-SCRIPT' | cat local_script.sh - input |
ssh host 'set some arguments; eval "$(sed -u "/^#END-OF-SCRIPT/q")"'
(aquí el código es evaluado por el shell de inicio de sesión del usuario remoto y se supone que es similar a Bourne)
Respuesta2
Aquí hay una solución más elegante que utiliza el operador 'here-strings' de BASH ('<<<')
ssh -t host "/bin/bash <(base64 --decode <<<'$(base64 < script)' )"
Toma el script, lo convierte a una cadena base64, en el comando SSH lo vuelve a convertir y se lo entrega al bash como un nombre de archivo de canalización.
Para que una secuencia de comandos intensifique un shell interactivo, prefije el 'nombre de archivo de canalización' con "--rcfile"
ssh -t host "/bin/bash --rcfile <(base64 --decode <<<'$(base64 < script)' )"
Más en... https://antofthy.gitlab.io/info/apps/ssh_remote_commands.txt
Respuesta3
Esto funciona bien e incluso te permite pasar el contenido de varios archivos al script:
ssh host "bash -s -- '$(cat local_file_1.txt)' '$(cat local_file_2.txt)'" < local_script.sh
La explicación es que ssh
se ejecutará bash -s
al iniciar sesión y los argumentos del script serán todo lo que sigue --
. Así $1
será el contenido de local_file_1.txt
y $2
será el contenido de local_file_2.txt
. bash -s
lee comandos de stdin que provienen de < local_script.sh
.