¿Cómo ejecuto un script bash local en máquinas remotas a través de ssh?

51

Estoy buscando una forma de impulsar la configuración de una máquina central a varias máquinas remotas sin la necesidad de instalar nada en las máquinas remotas.

El objetivo es hacer algo como lo haría con herramientas como cfengine, pero en un conjunto de máquinas que no tienen agentes configurados. En realidad, esta podría ser una buena técnica de configuración cfagenten un conjunto de máquinas remotas existentes.

tremoloqui
fuente
Similar en SO: stackoverflow.com/q/305035/435605
AlikElzin-kilaka
1
Las preguntas reales tienen 23 votos a favor, donde el duplicado en SO tiene 55: P
MoJo

Respuestas:

55

Puede pasar un script y hacer que se ejecute de manera efímera al conectarlo y ejecutar un shell.

p.ej

echo "ls -l; echo 'Hello World'" | ssh me@myserver /bin/bash

Naturalmente, la "ls -l; echo 'Hello World'"parte podría reemplazarse con un script bash almacenado en un archivo en la máquina local.

p.ej

cat script.sh | ssh me@myserver /bin/bash

¡Salud!

tremoloqui
fuente
2
¿Cómo hago que esto se ejecute como sudo en el sistema remoto? Ej. Si estuviera conectado al servidor remoto, normalmente lo ejecutaría como sudo -u testuser script.sh
Sharjeel
¿Qué pasa si el script al que llamo involucra interacciones del usuario?
Abhimanyu Srivastava
20

Hay varias formas de hacerlo.

1:

ssh user@remote_server 'bash -s' < localfile

2:

cat localfile  | ssh user@remote_server

3:

ssh user@remote_server "$(< localfile)"

el número 3 es mi forma preferida, permite comandos interactivos, por ejemplo su -S service nginx restart

(El n. ° 1 consumirá el resto del script como entrada para la pregunta de contraseña cuando lo use su -S).

Paul Verschoor
fuente
44
Con respecto a la ejecución de un script local en una máquina remota: ¿hay alguna forma de enviar variables como argumento a la máquina remota? es decir, junto con el script, quiero enviar una variable (que tenga varias líneas) como argumento a la máquina remota. El script entonces intenta usar la variable.
13

Recomendaría la tela de Python para este propósito:

#!/usr/bin/python
# ~/fabfile.py

from fabric_api import *

env.hosts = ['host1', 'host2']
def deploy_script():
    put('your_script.sh', 'your_script.sh', mode=0755)
    sudo('./your_script.sh')

# from shell
$ fab deploy_script

Debería poder usar lo anterior para comenzar. Consulte la excelente documentación de Fabric para hacer el resto. Como anexo, es totalmente posible escribir su secuencia de comandos completamente dentro de Fabric; no es necesario copiar, sin embargo, debe tenerse en cuenta que para cambiar la secuencia de comandos en todas las máquinas, solo necesitaría editar la copia local y volver a implementarla. Además, con un poco más que el uso básico de la API, puede modificar el script en función del host en el que se está ejecutando actualmente y / u otras variables. Es una especie de Esperanza pitónica.

Sam Halicke
fuente
No creo que esto responda exactamente la pregunta, pero me gusta la idea y podría ver Fabric como una herramienta útil.
tremoloqui
1
@tremoloqui Fabric es un envoltorio de python alrededor de ssh: no es necesario instalar nada en las máquinas de destino, salvo la secuencia de comandos que se empuja. Lo cual, si se reescribe como una serie de comandos de Fabric (usando runy sudo), ni siquiera es necesario.
Izkata
5

Esto es exactamente para lo que se usa Ansible. No hay agente, solo tiene que crear un archivo de texto llamado:

/etc/ansible/hosts

con contenido que se parece a:

[webhosts]
web[1-8]

Esto especificaría que las máquinas "web1, web2 ... web8" están en el grupo "webhosts". Entonces puedes hacer cosas como:

ansible webhosts -m service -a "name=apache2 state=restarted" --sudo

para reiniciar el servicio apache2 en todas sus máquinas, usando sudo.

Puedes hacer comandos sobre la marcha como:

ansible webhosts -m shell -a "df -h"

o puede ejecutar un script local en la máquina remota:

ansible webhosts -m script -a "./script.sh"

o puede crear un libro de jugadas (consulte la documentación para obtener más detalles) con una configuración completa a la que desea que se ajusten sus servidores y desplegarlo con:

ansible-playbook webplaybook.yml

Básicamente, puede comenzar a usarlo como una herramienta de línea de comandos para ejecutar comandos en varios servidores y expandir su uso a una herramienta de configuración completa como mejor le parezca.

seumasmac
fuente
3
Me gusta ansible tanto como el que más, pero si él está preguntando por una secuencia de comandos, a continuación, ansible tiene un muy buen módulo de script :ansible webhosts -m script script.sh
ptman
1
Todas las otras respuestas involucran un script bash, pero no es lo que él solicitó específicamente. Él solo dijo empujando la configuración a máquinas remotas. Pero buena mención del módulo de script :)
seumasmac
¡Por favor vote esta respuesta! ¡Esta es la manera de hacerlo!
pollitos
@ptman ¡Acabo de notar que si bien no menciona un guión en la pregunta, sí lo hace en el título! Lo siento. He actualizado
seumasmac
3

Como se explica en esta respuesta , puede usar heredoc :

ssh user@host <<'ENDSSH'
#commands to run on remote host
ENDSSH

Debe tener cuidado con heredoc, porque solo envía texto, pero en realidad no espera la respuesta. Eso significa que no esperará a que se ejecuten sus comandos.

BЈовић
fuente
1

La respuesta aquí ( https://stackoverflow.com/a/2732991/4752883 ) funciona muy bien si está intentando ejecutar un script en una máquina Linux remota usando plinko ssh. Funcionará si el script tiene varias líneas linux.

** Sin embargo, si está intentando ejecutar un script por lotes ubicado en una linux/windowsmáquina local y su máquina remota lo está Windows, y consta de varias líneas usando **

plink root@MachineB -m local_script.bat

No funcionará.

Solo se ejecutará la primera línea del script. Esto es probablemente una limitación de plink.

Solución 1:

Para ejecutar un script por lotes multilínea (especialmente si es relativamente simple, que consta de unas pocas líneas):

Si su secuencia de comandos por lotes original es la siguiente

cd C:\Users\ipython_user\Desktop 
python filename.py

puede combinar las líneas juntas usando el separador "&&" de la siguiente manera en su local_script.batarchivo de la siguiente manera https://stackoverflow.com/a/8055390/4752883 :

cd C:\Users\ipython_user\Desktop && python filename.py

Después de este cambio, puede ejecutar el script como se indica aquí por @ JasonR.Coombs: https://stackoverflow.com/a/2732991/4752883

Solución 2:

Si su secuencia de comandos por lotes es relativamente complicada, puede ser mejor utilizar una secuencia de comandos por lotes que encapsule el comando plink, así como sigue, como se indica aquí por @Martin https://stackoverflow.com/a/32196999/4752883 :

rem Open tunnel in the background
start plink.exe -ssh [username]@[hostname] -L 3307:127.0.0.1:3306 -i "[SSH
key]" -N

rem Wait a second to let Plink establish the tunnel 
timeout /t 1

rem Run the task using the tunnel
"C:\Program Files\R\R-3.2.1\bin\x64\R.exe" CMD BATCH qidash.R

rem Kill the tunnel
taskkill /im plink.exe
alpha_989
fuente
0

¿Por qué no simplemente copiar primero el script y luego ejecutarlo?

scp your_script.sh the_server:
ssh the_server "chmod +x your_script.sh; ./your_script.sh"

Por supuesto, debe tener cuidado de no cargarlo en un lugar de escritura mundial, para que nadie más pueda manipularlo antes de ejecutarlo (posiblemente como root).

Dave Vogt
fuente
1
La razón por la que no quiero cargar el script es para que no tenga que ser administrado y tenga los riesgos que usted mencionó. Además, me parece que es más simple que el proceso de varios pasos de carga, procesamiento y eliminación (opcionalmente).
tremoloqui
0

Vuelva a escribir la secuencia de comandos de manera que cada comando en ella ya tenga el prefijo ssh y un nombre de host / ip o una lista de tales se pase a la secuencia de comandos como argumento (suponiendo que tenga configurada la autenticación de clave sin contraseña / ssh-agent). Es posible que sea necesario algún trabajo para pasar correctamente los códigos de error / retorno de los comandos remotos ...

rackandboneman
fuente
0

Si el script no es demasiado grande, y está usando bash o ksh ...

ssh vm24 -t bash -c "$(printf "%q" "$(< shell-test.sh )")"

Tanto stdin como stdout funcionan correctamente, pero el script está limitado al tamaño del argumento (generalmente alrededor de 100k). Los argumentos del guión pueden funcionar, al final de la línea, posiblemente siguiendo un argumento "-" adicional. El "-t" para asignar una pty es opcional.

Cuidado: esto confunde bash-completar, no presionar la pestaña.

usuario3710044
fuente