Come usare SSH per eseguire uno script di shell su un computer remoto?


1223

Devo eseguire uno script di shell (Windows / Linux) su una macchina remota.

Ho SSH configurato su entrambe le macchine A e B. Il mio script è sulla macchina A che eseguirà parte del mio codice su una macchina remota, macchina B.

I computer locali e remoti possono essere basati su sistema Windows o Unix.

C'è un modo per eseguire farlo usando plink / ssh?


6
La stessa domanda è già su serverfault: serverfault.com/questions/215756/… Quindi probabilmente non ha senso migrare questa domanda.
sleske,

9
La domanda su Server Fault non ha però tante risposte. Forse questa domanda dovrebbe sostituirla.
Big McLarge, enorme

5
Questa risposta mi piace personalmente: unix.stackexchange.com/questions/87405/…
mikevoermans

27
Inoltre, dovrebbe ovviamente essere in tema poiché ssh è uno strumento privilegiato per lo sviluppo del software.
static_rtti

4
Le domande su caffè e ssh non condividono lo stesso grado di off-topicness su SO. Votato per riaprire.
Vincent Cantin,

Risposte:


1194

Se la macchina A è una finestra di Windows, è possibile utilizzare Plink (parte di PuTTY ) con il parametro -m ed eseguirà lo script locale sul server remoto.

plink root@MachineB -m local_script.sh

Se la macchina A è un sistema basato su Unix, è possibile utilizzare:

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

Non è necessario copiare lo script sul server remoto per eseguirlo.


11
c'è un vantaggio nell'usare l' -sopzione? questa pagina man mi porta a credere che elaborerà l'input standard quando avrà finito di elaborare le opzioni, che -ssiano usate o meno.
aeroNotAuto

79
Per uno script che richiede sudo, esegui ssh root@MachineB 'echo "rootpass" | sudo -Sv && bash -s' < local_script.sh.
bradley.ayers,

6
@ bradley.ayers ricordati di avviare il comando con uno 'spazio' per saltare la cronologia (PS devi HISTCONTROL=ignoreboth or ignorespacefarlo funzionare)
derenio

8
@ bradley.ayers in quali situazioni avresti bisogno di sudo se esegui già il login come root?
Brian Schlenker,

21
@Agostino, puoi aggiungere parametri come questo: i ssh root@MachineB ARG1="arg1" ARG2="arg2" 'bash -s' < local_script.sh crediti vanno completamente alla risposta di @chubbsondubs di seguito.
Yves Van Broekhoven,

635

Questa è una vecchia domanda e la risposta di Jason funziona bene, ma vorrei aggiungere questo:

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

Questo può anche essere usato con su e comandi che richiedono l'input dell'utente. (nota l' 'eredità fuggita)

Modifica: poiché questa risposta continua a ricevere frammenti di traffico, aggiungerei ancora più informazioni a questo meraviglioso uso di heredoc:

Puoi annidare i comandi con questa sintassi e questo è l'unico modo in cui l'annidamento sembra funzionare (in modo sano)

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

Puoi effettivamente avere una conversazione con alcuni servizi come telnet, ftp, ecc. Ma ricorda che heredoc invia semplicemente lo stdin come testo, non attende la risposta tra le righe

Modifica: ho appena scoperto che è possibile rientrare le parti interne con le schede se si utilizza <<-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

(Penso che dovrebbe funzionare)

Vedi anche http://tldp.org/LDP/abs/html/here-docs.html


4
puoi temporizzare un po 'aggiungendo righe come: # $ (sleep 5)
Olivier Dulac

50
notare che con virgolette singole attorno al terminatore ( <<'ENDSSH'), le stringhe non verranno espanse, le variabili non verranno valutate. Puoi anche usare <<ENDSSHo <<"ENDSSH"se vuoi l'espansione.
Maackle,

3
Expectpuò essere utilizzato quando è necessario automatizzare comandi interattivi come FTP.
programmi

5
Nota che avevo un Pseudo-terminal will not be allocated because stdin is not a terminal.messaggio. Bisogna usare ssh con i -t -tparametri per evitarlo. Vedi questa discussione su SO
Buzut

8
Se stai tentando di utilizzare la sintassi << - 'END', assicurati che il delimitatore di fine ereditario sia rientrato utilizzando i TAB, non gli spazi. Nota che copia / incolla da stackexchange ti darà spazi. Cambia quelli in schede e la funzione di rientro dovrebbe funzionare.
fbicknel,

249

Inoltre, non dimenticare di sfuggire alle variabili se desideri prenderle dall'host di destinazione.

Questo mi ha sorpreso in passato.

Per esempio:

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

stampa / home / user2

mentre

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

stampa / home / utente

Un altro esempio:

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

stampa "ciao" correttamente.


2
Tuttavia, tenere presente quanto segue: ssh user2@host 'bash -s' echo $HOME /home/user2 exit
errant.info,

1
Solo per aggiungere che nei forloop in esecuzione in sshsessione la variabile loop non deve essere sfuggita.
AlexeyDaryin,

1
In molte situazioni, il modo sano di risolvere il tuo ultimo esempio sarebbe quello di ssh user2@host2 'echo hello world' | awk '{ print $1 }'eseguire lo script Awk localmente. Se il comando remoto produce una quantità immensa di output, ovviamente si desidera evitare di copiarlo nuovamente sul server locale. Per inciso, le virgolette singole attorno al comando remoto evitano la necessità di scappare.
Tripleee,

151

Questa è un'estensione della risposta di YarekT per combinare i comandi remoti in linea con il passaggio delle variabili ENV dal computer locale all'host remoto in modo da poter parametrizzare gli script sul lato remoto:

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

L'ho trovato eccezionalmente utile conservando tutto in uno script in modo che sia molto leggibile e gestibile.

Perché questo funziona. ssh supporta la sintassi seguente:

ssh user @ host remote_command

In bash possiamo specificare le variabili di ambiente da definire prima di eseguire un comando su una singola riga in questo modo:

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

Ciò semplifica la definizione delle variabili prima di eseguire un comando. In questo caso echo è il nostro comando che stiamo eseguendo. Tutto prima dell'eco definisce le variabili di ambiente.

Quindi uniamo queste due funzionalità e la risposta di YarekT per ottenere:

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

In questo caso stiamo impostando ARG1 e ARG2 su valori locali. Invio di tutto dopo user @ host come telecomando_comando. Quando la macchina remota esegue il comando ARG1 e ARG2 vengono impostati i valori locali, grazie alla valutazione della riga di comando locale, che definisce le variabili di ambiente sul server remoto, quindi esegue il comando bash -s usando tali variabili. Ecco.


1
Nota che se vuoi passare argomenti come -a, allora puoi usare -. es. 'ssh user @ host - -a foo bar' bash -s '<script.sh'. E gli arg possono anche andare dopo il reindirizzamento, ad esempio 'ssh user @ host' bash -s '<script.sh - -a foo bar'.
martedì

8
Se uno qualsiasi dei valori var env contiene spazi, utilizzare:ssh user@host "ARG1=\"$ARG1\" ARG2=\"$ARG2\"" 'bash -s' <<'ENDSSH'...
TalkLittle

Come ho scritto in un altro commento, il punto stesso dell'utilizzo -sè quello di poter applicare argomenti agli script che provengono da stdin. Voglio dire, potresti anche ometterlo se non lo userai. Se lo usi, non c'è motivo di usare le variabili di ambiente:ssh user@host 'bash -s value1 value2' <<< 'echo "$@"'
JoL

104
<hostA_shell_prompt>$ ssh user@hostB "ls -la"

Questo ti chiederà la password, a meno che tu non abbia copiato la tua chiave pubblica dell'utente hostA nel file authorized_keys nella home directory dell'utente .ssh. Ciò consentirà l'autenticazione senza password (se accettata come metodo di autenticazione sulla configurazione del server ssh)


3
Ti ha votato. Questa è una soluzione valida Ovviamente, le chiavi devono essere protette, ma possono anche essere invalidate come una password dal lato server.
Willasaywhat

8
Non credo che questo risponda alla domanda. L'esempio mostra come eseguire un comando remoto, ma non come eseguire uno script locale su una macchina remota.
Jason R. Coombs,

Non sei sicuro, ma non puoi reindirizzare lo script su hostA per eseguirlo su hostB usando questo metodo?
nevets1219,

27

Ho iniziato a utilizzare Fabric per operazioni più sofisticate. Fabric richiede Python e un paio di altre dipendenze, ma solo sul computer client. Il server deve essere solo un server SSH. Trovo che questo strumento sia molto più potente degli script di shell trasmessi a SSH e vale la pena di essere configurato (in particolare se ti piace programmare in Python). Fabric gestisce l'esecuzione di script su più host (o host di determinati ruoli), aiuta a facilitare operazioni idempotenti (come l'aggiunta di una riga a uno script di configurazione, ma non se è già presente) e consente la costruzione di logiche più complesse (come Python la lingua può fornire).


11

Prova a correre ssh user@remote sh ./script.unx.


8
Funziona solo se lo script si trova nella directory predefinita (home) sul telecomando. Penso che la domanda sia come eseguire uno script memorizzato localmente sul telecomando.
metasim,

1
nome utente ssh @ ip "chmod + x script.sh" <br/> nome utente ssh @ ip "percorso del file sh nell'host remoto"
mani deepak



5

Uso questo per eseguire uno script di shell su una macchina remota (testato su / bin / bash):

ssh deploy@host . /home/deploy/path/to/script.sh

3
ssh user@hostname ".~/.bashrc;/cd path-to-file/;.filename.sh"

altamente raccomandato per l'origine del file di ambiente (.bashrc / .bashprofile / .profile). prima di eseguire qualcosa nell'host remoto perché le variabili di ambiente degli host di destinazione e di origine potrebbero essere più profonde.


Questo non spiega come spostare uno script locale sull'host remoto.
Kirelagin,

2

se si desidera eseguire un comando come questo temp=`ls -a` echo $temp comando in `` causerà errori.

il comando sotto risolverà questo problema ssh user@host ''' temp=`ls -a` echo $temp '''


1

La risposta qui ( https://stackoverflow.com/a/2732991/4752883 ) funziona benissimo se stai cercando di eseguire uno script su una macchina Linux remota usando plinko ssh. Funzionerà se lo script ha più righe linux.

** Tuttavia, se si sta tentando di eseguire uno script batch situato su un linux/windowscomputer locale e il computer remoto lo è Windows, ed è composto da più linee usando **

plink root@MachineB -m local_script.bat

non funzionerà.

Verrà eseguita solo la prima riga dello script. Questo è probabilmente un limite di plink.

Soluzione 1:

Per eseguire uno script batch multilinea (soprattutto se è relativamente semplice, composto da poche righe):

Se lo script batch originale è il seguente

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

puoi unire le linee usando il separatore "&&" come segue nel tuo local_script.batfile: https://stackoverflow.com/a/8055390/4752883 :

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

Dopo questa modifica, è quindi possibile eseguire lo script come indicato qui da @ JasonR.Coombs: https://stackoverflow.com/a/2732991/4752883 con:

`plink root@MachineB -m local_script.bat`

Soluzione 2:

Se lo script batch è relativamente complicato, potrebbe essere meglio utilizzare uno script batch che incapsuli il comando plink e come indicato qui di seguito da @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

1

Questo script bash fa ssh in una macchina remota di destinazione ed esegue alcuni comandi nella macchina remota, non dimenticare di installare aspettati prima di eseguirlo (su 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"

1
questo è ottimo se devi usare le password ... comunque a beneficio di chiunque guardi a casa qualsiasi comando ssh dovrebbe usare una coppia di chiavi pubbliche + private non password ... una volta sul posto aggiorna il tuo server ssh per chiudere completamente le password
Scott Stensland

-1

Puoi usare runoverssh :

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

-s esegue uno script locale in remoto


Flag utili:
-g usa una password globale per tutti gli host (richiesta password singola)
-nusa SSH invece di sshpass, utile per l'autenticazione con chiave pubblica


-24

Innanzitutto, copia lo script su Machine B usando scp

[user @ machineA] $ scp / path / to / script user @ machineB: / home / user / path

Quindi, esegui semplicemente lo script

[user @ machineA] $ ssh user @ machineB "/ home / utente / percorso / script"

Funzionerà se hai dato il permesso eseguibile allo script.


ciao ho applicato il suggerimento raccomandato ma mi dà il seguente errore [oracle @ node1 ~] $ ssh oracle @ node2: ./ home / oracle / au / fs / conn.sh ssh: node2: ./ home / oracle / au / fs / conn.sh: Nome o servizio non noto [oracle @ node1 ~] $

'ssh oracle @ node2: ./ home / oracle / au / fs / conn.sh'? Riga di comando errata, il nome del comando deve essere separato dalla parte user @ host con uno spazio, non due punti.
Bortzmeyer,

5
Sto eseguendo il downvoting di questo perché la richiesta principale che non può essere eseguita senza copiarlo è errata.
Jason R. Coombs,

Sarebbe stato utile se Jason avesse aggiunto il motivo per cui non è corretto invece di dichiarare semplicemente il fatto. Inutile.
Kris,

[user @ machineA] $ ssh root @ MachineB 'bash -s' </ machinea / path / to / script
Oleksii Kyslytsyn
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.