Qual è il modo più pulito per eseguire ssh ed eseguire più comandi in Bash?


349

Ho già impostato un agente ssh e posso eseguire comandi su un server esterno nello script Bash facendo cose come:

ssh blah_server "ls; pwd;"

Ora, quello che mi piacerebbe davvero fare è eseguire molti comandi lunghi su un server esterno. Racchiudere tutte queste virgolette tra virgolette sarebbe piuttosto brutto, e preferirei davvero evitare di ripetere più volte solo per evitarlo.

Quindi, c'è un modo in cui posso farlo in una sola volta racchiuso tra parentesi o qualcosa del genere? Sto cercando qualcosa sulla falsariga di:

ssh blah_server (
   ls some_folder;
   ./someaction.sh;
   pwd;
)

Fondamentalmente, sarò felice con qualsiasi soluzione purché sia ​​pulita.

modificare

Per chiarire, sto parlando di far parte di uno script bash più ampio. Altre persone potrebbero aver bisogno di occuparsi della sceneggiatura, quindi mi piacerebbe tenerla pulita. Non voglio avere uno script bash con una riga che assomigli a:

ssh blah_server "ls some_folder; ./someaction.sh 'some params'; pwd; ./some_other_action 'other params';"

perché è estremamente brutto e difficile da leggere.


2
Hmm, che ne dici di mettere tutto questo in uno script sul server e chiamarlo con una sola chiamata ssh?
Nikolai Fetissov,

@Nikolai se i comandi dipende dal lato client, che può essere scritto in uno script di shell, quindi scp, sshe correre. Questo sarà il modo più pulito, penso.
Khachik,

6
Questo fa parte di uno script bash più grande, quindi preferirei non dividerlo con metà vivente sul mio personal computer e l'altra metà vivente sul server ed eseguire ssh. Se possibile, mi piacerebbe davvero tenerlo come uno script eseguito dal mio personal computer. Non c'è davvero un modo pulito per racchiudere un gruppo di comandi in un ssh?
Eli,

Il modo migliore non è usare bash ma Perl, Python, Ruby, ecc.
salva

1
Perché vuoi evitare di mettere i comandi remoti tra virgolette? Puoi avere newline all'interno delle virgolette, quante ne vuoi; e l'uso di una stringa anziché di input standard significa che è disponibile l'input standard per la lettura dell'input nello script remoto. (Anche se su Unix, le virgolette singole sono generalmente preferite rispetto alle virgolette doppie, a meno che tu non abbia specificamente bisogno della shell locale per valutare alcune parti della stringa.)
tripleee

Risposte:


456

Che ne dici di un documento Bash Here :

ssh otherhost << EOF
  ls some_folder; 
  ./someaction.sh 'some params'
  pwd
  ./some_other_action 'other params'
EOF

Per evitare i problemi menzionati da @Globalz nei commenti, potresti essere in grado (a seconda di cosa stai facendo sul sito remoto) di cavartela sostituendo la prima riga con

ssh otherhost /bin/bash << EOF

Si noti che è possibile effettuare la sostituzione variabile nel documento Qui, ma potrebbe essere necessario affrontare i problemi di quotazione. Ad esempio, se si cita la "stringa limite" (ad es. EOFSopra), non è possibile effettuare sostituzioni variabili. Ma senza citare la stringa limite, le variabili vengono sostituite. Ad esempio, se hai definito $NAMEsopra nel tuo script di shell, potresti farlo

ssh otherhost /bin/bash << EOF
touch "/tmp/${NAME}"
EOF

e creerebbe un file sulla destinazione otherhostcon il nome di qualunque cosa tu abbia assegnato $NAME. Si applicano anche altre regole sulla citazione degli script di shell, ma sono troppo complicate per entrare qui.


6
Sembra esattamente quello che voglio! Come funziona Ti capita di avere un link a una pagina che lo spiega?
Eli,

9
+1 Stavo solo pensando a me stesso - ecco un posto dove leggerlo: tldp.org/LDP/abs/html/here-docs.html
bosmacs

14
Ricevo anche questo output sul mio locale: lo pseudo-terminale non verrà allocato perché lo stdin non è un terminale.
Globalz,

14
Potrebbe essere importante citare la parola (ovvero il primo "EOF") per impedire l'espansione delle righe di comando. Vedi: man bash | meno + / 'Here Documents'
assem

6
Paul, lo consiglio solo perché con quasi 200k visualizzazioni su questa domanda, sembra che molte persone vengano qui quando scrivono con ssh. È comune dover inserire valori durante lo scripting. Se non fosse per il rumore nelle altre domande e commenti, ne farei solo uno e sarei sulla buona strada, ma è improbabile che si veda in questa fase. Una nota a una riga potrebbe salvare alla gente alcuni grossi mal di testa.
Koyae,

122

Modifica lo script localmente, quindi esegui il pipe in ssh, ad es

cat commands-to-execute-remotely.sh | ssh blah_server

dove commands-to-execute-remotely.shappare come il tuo elenco sopra:

ls some_folder
./someaction.sh
pwd;

7
Questo ha il grande vantaggio di sapere esattamente cosa viene eseguito dallo script remoto - nessun problema con la quotazione. Se hai bisogno di comandi dinamici, puoi usare uno script di shell con una subshell, eseguendo comunque il piping in ssh, ovvero ( echo $mycmd $myvar ; ...) | ssh myhost- come per l'uso di cat, sai esattamente cosa sta succedendo nel flusso di comandi ssh. E ovviamente la subshell nello script può essere multi-linea per leggibilità - vedi linuxjournal.com/content/bash-sub-shells
RichVel

6
Puoi farlo con argomenti nel commands-to-execute-remotely.sh?
elaRosca,

1
Penso che questo sia molto più utile della soluzione di paultomblin. Poiché lo script può essere reindirizzato a qualsiasi server SSH senza dover aprire il file. Inoltre è molto più leggibile.
Patrick Bassut,

3
sì, ma usando echo o un documento qui (vedi risposta in alto): usa: $localvarper interpretare una variabile definita localmente, \$remotevarper interpretare in remoto una variabile definita da remoto, \$(something with optionnal args)per ottenere l'output di qualcosa eseguito sul server remoto. Un esempio che puoi vedere attraverso 1 (o, come mostrato qui, più comandi SSH):echo " for remotedir in /*/${localprefix}* ; do cd \"\$remotedir\" && echo \"I am now in \$(pwd) on the remote server \$(hostname) \" ; done " | ssh user1@hop1 ssh user2@hop2 ssh user@finalserver bash
Olivier Dulac

2
Hmm ... in che cosa differisce dall'uso del reindirizzamento di input, cioè ssh blah_server < commands-to-execute-remotely.sh?
flow2k,

41

Per abbinare il codice di esempio, è possibile racchiudere i comandi all'interno di qoutes singoli o doppi. Per esempio

ssh blah_server "
  ls
  pwd
"

Mi piace questo formato, ma purtroppo non è utile per archiviare i dati standard in una variabile.
Signus,

1
Signus, cosa intendi con "memorizzazione dei dati standard in una variabile"?
Andrei B,

1
@Signus È perfettamente possibile fare ciò che descrivi, anche se probabilmente vorrai usare virgolette singole invece di doppie virgolette attorno ai comandi remoti (o sfuggire agli operatori che devono essere sfuggiti all'interno di virgolette doppie per impedire l'intercettazione della shell locale e interpolandoli).
Tripleee,

Non riesco a inserire all'interno del codice di virgolette doppie un comando come questo fileToRemove = $ (find. -Type f -name 'xxx.war'). fileToRemove dovrebbe avere un nome file all'interno ma invece ha una stringa vuota. Qualcosa deve essere evitato?
jkonst

37

Vedo due modi:

Per prima cosa crei una presa di controllo come questa:

 ssh -oControlMaster=yes -oControlPath=~/.ssh/ssh-%r-%h-%p <yourip>

ed esegui i tuoi comandi

 ssh -oControlMaster=no -oControlPath=~/.ssh/ssh-%r-%h-%p <yourip> -t <yourcommand>

In questo modo è possibile scrivere un comando ssh senza riconnettersi effettivamente al server.

Il secondo sarebbe generare dinamicamente lo script, scpinglobandolo ed eseguendolo.


20

Questo può anche essere fatto come segue. Inserisci i tuoi comandi in uno script, chiamiamolo command-inc.sh

#!/bin/bash
ls some_folder
./someaction.sh
pwd

Salva il file

Ora eseguilo sul server remoto.

ssh user@remote 'bash -s' < /path/to/commands-inc.sh

Non ho mai fallito per me.


Simile a quello che stavo pensando originariamente! Ma perché è bash -snecessario?
flow2k,

Inoltre, è #!/bin/bashdavvero usato?
flow2k,

2
-S è lì per compatibilità. Da man bash -s Se l'opzione -s è presente o se non rimangono argomenti dopo l'elaborazione dell'opzione, i comandi vengono letti dall'input standard. Questa opzione consente di impostare i parametri posizionali quando si richiama una shell interattiva.
RJ

1
RJ, grazie! Con il mio primo commento, sembra che lo facciamo ssh user@remote < /path/to/commands-inc.sh(sembra funzionare per me). Bene, immagino che la tua versione assicuri che usiamo la bashshell e non qualche altra shell - questo è lo scopo, non è vero?
flow2k,

2
Grazie. Sì, alcuni di noi hanno le nostre note e le abitudini delle versioni precedenti di bash e altre shell. :-)
RJ

10

Inserisci tutti i comandi su uno script e può essere eseguito come

ssh <remote-user>@<remote-host> "bash -s" <./remote-commands.sh

Un po 'strano ma funziona bene. E args può essere passato allo script (prima o dopo il reindirizzamento). es. 'ssh <remote-user> @ <remote-host> "bash -s" arg1 <./ remote-commands.sh arg2' eg 'ssh <remote-user> @ <remote-host> "bash -s" - - -arg1 <./ remote-commands.sh arg2 '
gaoithe

Ho avuto una domanda simile con un'altra risposta sopra, ma qual è lo scopo di includere bash -s?
flow2k,

1
@ flow2k -s indica la bash per leggere i comandi dall'input standard. Ecco la descrizione dalle manpagineIf the -s option is present, or if no arguments remain after option processing, then commands are read from the standard input. This option allows the positional parameters to be set when invoking an interactive shell.
Jai Prakash,

@JaiPrakash Grazie per questo, ma stavo davvero pensando se potremmo semplicemente eliminare del bashtutto il comando, cioè ssh remote-user@remote-host <./remote-commands.sh. Ho provato la mia strada e sembrava funzionare, anche se non sono sicuro che sia carente in qualche modo.
flow2k,

@ flow2k dalla mia comprensione delle pagine man non ci sarà alcuna carenza senza quell'opzione. È necessario se si sta tentando di trasmettere argomenti. - grazie
Jai Prakash

9

SSH ed esegui più comandi in Bash.

Separare i comandi con punto e virgola all'interno di una stringa, passati a echo, tutti inviati al comando ssh. Per esempio:

echo "df -k;uname -a" | ssh 192.168.79.134

Pseudo-terminal will not be allocated because stdin is not a terminal.
Filesystem     1K-blocks    Used Available Use% Mounted on
/dev/sda2       18274628 2546476  14799848  15% /
tmpfs             183620      72    183548   1% /dev/shm
/dev/sda1         297485   39074    243051  14% /boot
Linux newserv 2.6.32-431.el6.x86_64 #1 SMP Sun Nov 10 22:19:54 EST 2013 x86_64 x86_64 x86_64 GNU/Linux

arnab, in futuro, descrivi brevemente cosa fa il tuo codice. Quindi parla di come funziona, quindi inserisci il codice. Quindi inserisci il codice in un blocco di codice in modo che sia facile da leggere.
Eric Leschinski,

Dalla domanda, hai offerto esattamente ciò che l'OP vuole evitare: "Non voglio avere uno script bash con una riga che assomigli ..."
jww

6

Per chiunque inciampare qui come me - ho avuto successo sfuggendo al punto e virgola e alla nuova riga:

Primo passo: il punto e virgola. In questo modo, non rompiamo il comando ssh:

ssh <host> echo test\;ls
                    ^ backslash!

Elencato la directory host / home remota (accesso come root), mentre

ssh <host> echo test;ls
                    ^ NO backslash

elencato la directory di lavoro corrente.

Prossimo passo: spezzare la linea:

                      v another backslash!
ssh <host> echo test\;\
ls

Questo di nuovo elencava la directory di lavoro remota - formattazione migliorata:

ssh <host>\
  echo test\;\
  ls

Se davvero più bello di qui documento o citazioni intorno a linee spezzate - beh, non sono io a decidere ...

(Usando bash, Ubuntu 14.04 LTS.)


1
Cosa c'è di meglio che puoi usare && e || anche in questo modo - echo test \ & \ & ls
Miro Kropacek

@MiroKropacek Anche questo è carino. Meglio? Dipende da cosa vuoi; esegui il secondo comando in modo condizionale , quindi sì, eseguilo incondizionatamente (non importa se il primo ha avuto esito positivo o negativo), quindi non ...
Aconcagua

@MiroKropacek, non ho dovuto sfuggire a && quando ho messo virgolette doppie attorno ai comandi:ssh host "echo test && ls"
not2savvy

5

Le risposte postate usando stringhe multilinea e script bash multipli non hanno funzionato per me.

  • Le stringhe multilinea lunghe sono difficili da mantenere.
  • Script bash separati non mantengono le variabili locali.

Ecco un modo funzionale per ssh ed eseguire più comandi mantenendo il contesto locale.

LOCAL_VARIABLE=test

run_remote() {
    echo "$LOCAL_VARIABLE"
    ls some_folder; 
    ./someaction.sh 'some params'
    ./some_other_action 'other params'
}

ssh otherhost "$(set); run_remote"

3
Stai pensando a questo come se l'unica ragione per cui qualcuno vorrebbe fare questo è se sono seduti a una riga di comando. Ci sono anche altri motivi comuni per questa domanda, come l'esecuzione di comandi in ambienti distribuiti con strumenti come rundeck, jenkins, ecc.
Charles Addis,

funziona ma ricevo messaggi di errore indesiderati
Annahri,

5

Non sono sicuro se il più pulito per comandi lunghi ma sicuramente il più semplice:

ssh user@host "cmd1; cmd2; cmd3"

Il meglio della semplicità!
not2savvy

4

Funziona bene per la creazione di script, in quanto non è necessario includere altri file:

#!/bin/bash
ssh <my_user>@<my_host> "bash -s" << EOF
    # here you just type all your commmands, as you can see, i.e.
    touch /tmp/test1;
    touch /tmp/test2;
    touch /tmp/test3;
EOF

La parte "$ (che bash) -s" ti dà la posizione di bash sul computer locale, piuttosto che sul computer remoto. Penso che tu voglia invece '$ (che bash) -s' (virgolette singole per sopprimere la sostituzione dei parametri locali).
jsears,

1
Paul Tomblin ha fornito la risposta al documento Bash Here 6 anni prima. Quale valore viene aggiunto da questa risposta?
jww

-sbandiera, gli dico che potresti riuscire a cavartela sostituendo la prima linea con ... , e non sono riuscito a cavarmela solo chiamando bash che ricordo vagamente.
sjas,

4

Il modo più semplice per configurare il sistema in modo da utilizzare singole sessioni ssh per impostazione predefinita con il multiplexing.

Questo può essere fatto creando una cartella per i socket:

mkdir ~/.ssh/controlmasters

E quindi aggiungendo quanto segue alla tua configurazione .ssh:

Host *
    ControlMaster auto
    ControlPath ~/.ssh/controlmasters/%r@%h:%p.socket
    ControlMaster auto
    ControlPersist 10m

Ora, non è necessario modificare alcun codice. Ciò consente a più chiamate a ssh e scp senza creare più sessioni, il che è utile quando è necessaria una maggiore interazione tra i computer locali e remoti.

Grazie alla risposta di @ terminus, http://www.cyberciti.biz/faq/linux-unix-osx-bsd-ssh-multiplexing-to-speed-up-ssh-connections/ e https://en.wikibooks.org / wiki / OpenSSH / Cookbook / Multiplexing .

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.