Ejecute un script local con un archivo de entrada local en un host remoto

Ejecute un script local con un archivo de entrada local en un host remoto

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 sshproporciona 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 sshcliente pasa la LC_*variable ( SendEnvin ssh_config) y el sshdservidor las acepta ( AcceptEnvin sshd_config))

O simplemente pase el contenido de local_script.shcomo 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 sshla entrada estándar como con:

{
  echo '{'; cat local_script.sh; echo '}; exit'
  cat input
} | ssh host 'bash -s some arguments'

Aquí, el uso de bashporque bashse encargará de leer la entrada un byte a la vez para no leer más allá de esa }; exitlínea mientras que no todos los demás shells lo hacen.

O si seden 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 sshse ejecutará bash -sal iniciar sesión y los argumentos del script serán todo lo que sigue --. Así $1será el contenido de local_file_1.txty $2será el contenido de local_file_2.txt. bash -slee comandos de stdin que provienen de < local_script.sh.

información relacionada