¿Cómo usar SSH para ejecutar un script de shell en una máquina remota?

1223

Tengo que ejecutar un script de shell (Windows / Linux) en una máquina remota.

Tengo SSH configurado en la máquina A y B. Mi script está en la máquina A, que ejecutará parte de mi código en una máquina remota, la máquina B.

Las computadoras locales y remotas pueden ser sistemas basados ​​en Windows o Unix.

¿Hay alguna manera de ejecutar esto usando plink / ssh?

alpha_989
fuente
66
La misma pregunta ya está en serverfault: serverfault.com/questions/215756/… Entonces, probablemente no tenga sentido migrar esta pregunta.
sleske
99
Sin embargo, la pregunta sobre Falla del servidor no tiene tantas respuestas. Tal vez esta pregunta debería reemplazar a esa.
Big McLargeHuge
55
Me gusta esta respuesta personalmente: unix.stackexchange.com/questions/87405/…
mikevoermans
27
Además, obviamente debería estar en el tema ya que ssh es una herramienta principal para el desarrollo de software.
static_rtti
44
Las preguntas de café y ssh no comparten el mismo grado de falta de tema en SO. Votado para reabrir.
Vincent Cantin el

Respuestas:

1194

Si la Máquina A es un cuadro de Windows, puede usar Plink (parte de PuTTY ) con el parámetro -m, y ejecutará el script local en el servidor remoto.

plink root@MachineB -m local_script.sh

Si la máquina A es un sistema basado en Unix, puede usar:

ssh root@MachineB 'bash -s' < local_script.sh

No debería tener que copiar el script al servidor remoto para ejecutarlo.

Jason R. Coombs
fuente
11
¿Hay alguna ventaja en usar la -sopción? Esta página de manual me lleva a creer que procesará la entrada estándar cuando termine de procesar las opciones, ya -ssea que se use o no.
aeroNotAuto
79
Para un script que requiera sudo, ejecute ssh root@MachineB 'echo "rootpass" | sudo -Sv && bash -s' < local_script.sh.
bradley.ayers
66
@ bradley.ayers Recuerde que debe comenzar el comando con un 'espacio' para saltar la historia (PS que necesita tener HISTCONTROL=ignoreboth or ignorespacepara que funcione)
derenio
8
@ bradley.ayers ¿en qué situaciones necesitaría sudo si ya está iniciando sesión como root?
Brian Schlenker
21
@ Agostino, puede agregar parámetros como este: los ssh root@MachineB ARG1="arg1" ARG2="arg2" 'bash -s' < local_script.sh créditos van completamente a la respuesta de @chubbsondubs a continuación.
Yves Van Broekhoven
635

Esta es una vieja pregunta, y la respuesta de Jason funciona bien, pero me gustaría agregar esto:

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

Esto también se puede usar con su y comandos que requieren la entrada del usuario. (tenga en cuenta el 'heredoc escapado)

Editar: Dado que esta respuesta sigue recibiendo fragmentos de tráfico, agregaría aún más información a este maravilloso uso de heredoc:

Puede anidar comandos con esta sintaxis, y esa es la única forma en que la anidación parece funcionar (de una manera sensata)

ssh user@host <<'ENDSSH'
#commands to run on remote host
ssh user@host2 <<'END2'
# Another bunch of commands on another host
wall <<'ENDWALL'
Error: Out of cheese
ENDWALL
ftp ftp.secureftp-test.com <<'ENDFTP'
test
test
ls
ENDFTP
END2
ENDSSH

De hecho, puede tener una conversación con algunos servicios como telnet, ftp, etc. Pero recuerde que heredoc simplemente envía el stdin como texto, no espera la respuesta entre líneas

Editar: ¡Acabo de descubrir que puedes sangrar el interior con pestañas si lo usas <<-END!

ssh user@host <<-'ENDSSH'
    #commands to run on remote host
    ssh user@host2 <<-'END2'
        # Another bunch of commands on another host
        wall <<-'ENDWALL'
            Error: Out of cheese
        ENDWALL
        ftp ftp.secureftp-test.com <<-'ENDFTP'
            test
            test
            ls
        ENDFTP
    END2
ENDSSH

(Creo que esto debería funcionar)

Ver también http://tldp.org/LDP/abs/html/here-docs.html

Yarek T
fuente
44
puedes temporizar un poco agregando líneas como: # $ (dormir 5)
Olivier Dulac
50
tenga en cuenta que con comillas simples alrededor del terminador ( <<'ENDSSH'), las cadenas no se expandirán, las variables no se evaluarán. También puede usar <<ENDSSHo <<"ENDSSH"si desea expansión.
maackle
3
Expectse puede usar cuando necesita automatizar comandos interactivos como FTP.
programaths
55
Tenga en cuenta que tenía un Pseudo-terminal will not be allocated because stdin is not a terminal.mensaje. Uno tiene que usar ssh con -t -tparams para evitar eso. Ver este hilo en SO
Buzut
8
Si está intentando utilizar la sintaxis << - 'FIN', asegúrese de que el delimitador final heredoc esté sangrado usando TAB, no espacios. Tenga en cuenta que copiar / pegar desde stackexchange le dará espacios. Cambie esos a pestañas y la función de sangría debería funcionar.
fbicknel
249

Además, no olvide escapar de las variables si desea recogerlas del host de destino.

Esto me ha pillado en el pasado.

Por ejemplo:

user@host> ssh user2@host2 "echo \$HOME"

imprime / inicio / usuario2

mientras

user@host> ssh user2@host2 "echo $HOME"

imprime / inicio / usuario

Otro ejemplo:

user@host> ssh user2@host2 "echo hello world | awk '{print \$1}'"

imprime "hola" correctamente.

dogbane
fuente
2
Sin embargo, tenga en cuenta lo siguiente: ssh user2@host 'bash -s' echo $HOME /home/user2 exit
errant.info
1
Solo para agregar que en los forbucles que se ejecutan en la sshsesión, la variable del bucle no debe escaparse.
AlexeyDaryin
1
En muchas situaciones, la forma sensata de arreglar su último ejemplo sería, es ssh user2@host2 'echo hello world' | awk '{ print $1 }'decir, ejecutar el script Awk localmente. Si el comando remoto produce una inmensa cantidad de resultados, debe evitar copiarlo todo en el servidor local, por supuesto. Por cierto, las comillas simples alrededor del comando remoto evitan la necesidad de escapar.
tripleee
151

Esta es una extensión de la respuesta de YarekT para combinar comandos remotos en línea con pasar variables ENV desde la máquina local al host remoto para que pueda parametrizar sus scripts en el lado remoto:

ssh user@host ARG1=$ARG1 ARG2=$ARG2 'bash -s' <<'ENDSSH'
  # commands to run on remote host
  echo $ARG1 $ARG2
ENDSSH

Encontré esto excepcionalmente útil al mantenerlo todo en un solo script, por lo que es muy fácil de leer y mantener.

¿Por qué esto funciona? ssh admite la siguiente sintaxis:

ssh user @ host remote_command

En bash podemos especificar variables de entorno para definir antes de ejecutar un comando en una sola línea de esta manera:

ENV_VAR_1 = 'value1' ENV_VAR_2 = 'value2' bash -c 'echo $ ENV_VAR_1 $ ENV_VAR_2'

Eso facilita la definición de variables antes de ejecutar un comando. En este caso, echo es nuestro comando que estamos ejecutando. Todo antes de echo define variables de entorno.

Entonces combinamos esas dos características y la respuesta de YarekT para obtener:

ssh user @ host ARG1 = $ ARG1 ARG2 = $ ARG2 'bash -s' << 'ENDSSH' ...

En este caso, estamos configurando ARG1 y ARG2 a valores locales. Enviar todo después de user @ host como remote_command. Cuando la máquina remota ejecuta el comando ARG1 y ARG2, se establecen los valores locales, gracias a la evaluación de la línea de comando local, que define las variables de entorno en el servidor remoto, luego ejecuta el comando bash -s usando esas variables. Voila

chubbsondubs
fuente
1
Tenga en cuenta que si desea pasar argumentos como -a, puede usar -. por ejemplo, 'ssh user @ host - -a foo bar' bash -s '<script.sh'. Y los argumentos también pueden ir después de la redirección, por ejemplo, 'ssh user @ host' bash -s '<script.sh - -a foo bar'.
Gaoithe
8
Si alguno de los valores de env var contiene espacios, use:ssh user@host "ARG1=\"$ARG1\" ARG2=\"$ARG2\"" 'bash -s' <<'ENDSSH'...
TalkLittle
Como escribí en otro comentario, el punto de usar -ses poder aplicar argumentos a los scripts que se obtienen a través de stdin. Quiero decir, también puedes omitirlo si no lo vas a usar. Si lo usa, no hay razón para usar variables de entorno:ssh user@host 'bash -s value1 value2' <<< 'echo "$@"'
JoL
104
<hostA_shell_prompt>$ ssh user@hostB "ls -la"

Eso le solicitará una contraseña, a menos que haya copiado la clave pública de su usuario HostA en el archivo autorizado_claves en la página de inicio del directorio del usuario .ssh. Eso permitirá la autenticación sin contraseña (si se acepta como método de autenticación en la configuración del servidor ssh)

Vinko Vrsalovic
fuente
3
Te voté. Esta es una solución válida. Obviamente, las claves deben protegerse, pero también pueden invalidarse como una contraseña a través del servidor.
willasaywhat
8
No creo que esto responda la pregunta. El ejemplo muestra cómo ejecutar un comando remoto, pero no cómo ejecutar un script local en una máquina remota.
Jason R. Coombs
¿No está seguro, pero no puede canalizar su script en hostA para que se ejecute en hostB usando este método?
nevets1219
27

Empecé a usar Fabric para operaciones más sofisticadas. Fabric requiere Python y algunas otras dependencias, pero solo en la máquina cliente. El servidor solo necesita ser un servidor ssh. Considero que esta herramienta es mucho más poderosa que los scripts de shell entregados a SSH, y bien vale la pena la instalación (especialmente si disfrutas de la programación en Python). Fabric maneja la ejecución de scripts en múltiples hosts (o hosts de ciertos roles), ayuda a facilitar operaciones idempotentes (como agregar una línea a un script de configuración, pero no si ya está allí), y permite la construcción de una lógica más compleja (como Python el lenguaje puede proporcionar).

Jason R. Coombs
fuente
11

Intenta correr ssh user@remote sh ./script.unx.

Jeremy
fuente
8
Esto solo funciona si el script está en el directorio predeterminado (inicio) en el control remoto. Creo que la pregunta es cómo ejecutar un script almacenado localmente en el control remoto.
metasim
1
ssh username @ ip "chmod + x script.sh" <br/> ssh username @ ip "ruta al archivo sh en el host remoto"
mani deepak
10
cat ./script.sh | ssh <user>@<host>
cglotr
fuente
1
Muy interesante ...
Noah Broyles
5

Utilizo este para ejecutar un script de shell en una máquina remota (probado en / bin / bash):

ssh deploy@host . /home/deploy/path/to/script.sh
Es ma
fuente
3
ssh user@hostname ".~/.bashrc;/cd path-to-file/;.filename.sh"

Muy recomendable para obtener el archivo de entorno (.bashrc / .bashprofile / .profile). antes de ejecutar algo en el host remoto porque las variables de entorno de los hosts de destino y de origen pueden diferirse.

Ankush Sahu
fuente
Esto no explica cómo mover un script local al host remoto.
kirelagin
2

si desea ejecutar un comando como este temp=`ls -a` echo $temp en `` causará errores.

El siguiente comando resolverá este problema ssh user@host ''' temp=`ls -a` echo $temp '''

Jinmiao Luo
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 funciona

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: 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 con:

`plink root@MachineB -m local_script.bat`

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
1

Este script bash hace ssh en una máquina remota de destino, y ejecuta algún comando en la máquina remota, no olvide instalar esperar antes de ejecutarlo (en mac brew install expect)

#!/usr/bin/expect
set username "enterusenamehere"
set password "enterpasswordhere"
set hosts "enteripaddressofhosthere"
spawn ssh  $username@$hosts
expect "$username@$hosts's password:"
send -- "$password\n"
expect "$"
send -- "somecommand on target remote machine here\n"
sleep 5
expect "$"
send -- "exit\n"
Mohammed Rafeeq
fuente
1
esto es genial si debe usar contraseñas ... sin embargo, para el beneficio de cualquiera que esté mirando en casa, cualquier comando ssh debe usar un par de claves públicas + privadas, no contraseñas ... una vez en su lugar, actualice su servidor ssh para cerrar las contraseñas por completo
Scott Stensland
-1

Puedes usar runoverssh :

sudo apt install runoverssh
runoverssh -s localscript.sh user host1 host2 host3...

-s ejecuta un script local de forma remota


Indicadores útiles:
-g use una contraseña global para todos los hosts (solicitud de contraseña única)
-nuse SSH en lugar de sshpass, útil para la autenticación de clave pública

ahora en
fuente
-24

Primero, copie el script en la Máquina B usando scp

[usuario @ máquinaA] $ scp / ruta / a / script usuario @ máquinaB: / inicio / usuario / ruta

Luego, solo ejecuta el script

[usuario @ máquinaA] $ ssh usuario @ máquinaB "/ inicio / usuario / ruta / script"

Esto funcionará si ha dado permiso ejecutable al script.

Baishampayan Ghose
fuente
hola apliqué la sugerencia recomendada pero me dio el siguiente error [oracle @ node1 ~] $ ssh oracle @ node2: ./ home / oracle / au / fs / conn.sh ssh: node2: ./ home / oracle / au / fs / conn.sh: Nombre o servicio desconocido [oracle @ node1 ~] $
'ssh oracle @ node2: ./ home / oracle / au / fs / conn.sh'? Línea de comando incorrecta, el nombre del comando debe separarse de la parte usuario @ host con un espacio, no dos puntos.
bortzmeyer
55
Estoy rechazando esto porque es el reclamo principal de que no se puede ejecutar sin copiarlo es incorrecto.
Jason R. Coombs
Hubiera sido útil si Jason agregó por qué eso es incorrecto en lugar de simplemente decir el hecho. Inútil
Kris
[user @ machineA] $ ssh root @ MachineB 'bash -s' </ machinea / path / to / script
Oleksii Kyslytsyn