Passa i comandi come input a un altro comando (su, ssh, sh, ecc.)

Ho uno script in cui ho bisogno di avviare un comando, quindi passare alcuni comandi aggiuntivi come comandi a quel comando. Provai

su echo I should be root now: who am I exit echo done. 

… ma non funziona: il su riesce, ma il prompt dei comandi mi sta solo fissando. Se scrivo exit al prompt, l’ echo e who am i etc iniziano l’esecuzione! E l’ echo done. non viene eseguito affatto.

Allo stesso modo, ho bisogno che questo funzioni su ssh :

 ssh remotehost # this should run under my account on remotehost su ## this should run as root on remotehost whoami exit ## back exit # back 

Come lo risolvo?

Sto cercando risposte che risolvono questo in modo generale e che non sono specifici per su o ssh in particolare. L’intento è che questa domanda diventi canonica per questo particolare modello.

Uno script di shell è una sequenza di comandi. La shell leggerà il file di script ed eseguirà questi comandi uno dopo l’altro.

Nel solito caso, non ci sono sorprese qui; ma un frequente errore del principiante presuppone che alcuni comandi possano prendere il sopravvento dalla shell e iniziare a eseguire i seguenti comandi nel file di script anziché nella shell che sta attualmente eseguendo questo script. Ma non è così che funziona.

Fondamentalmente, gli script funzionano esattamente come i comandi interattivi, ma il modo in cui funzionano esattamente deve essere compreso correttamente. Interagentemente, la shell legge un comando (dall’input standard), esegue quel comando (con input da input standard) e quando ha finito legge un altro comando (da input standard).

Ora, quando si esegue uno script, l’input standard è ancora il terminale (a meno che non si sia utilizzato un reindirizzamento), ma i comandi vengono letti dal file di script, non dallo standard input. (L’opposto sarebbe davvero ingombrante – ogni read consumerebbe la riga successiva della sceneggiatura, cat sfogherebbe tutto il resto dello script e non ci sarebbe modo di interagire con esso!) Il file di script contiene solo i comandi per istanza di shell che la esegue (sebbene tu possa naturalmente usare ancora un documento qui ecc per incorporare gli input come argomenti del comando).

In altre parole, questi comandi “incompresi” ( su , ssh , sh , sudo , bash ecc.) Quando vengono eseguiti da soli (senza argomenti) avviano una shell intertriggers e, in una sessione intertriggers, ciò è ovvio; ma quando viene eseguito da una sceneggiatura, spesso non è quello che vuoi.

Tutti questi comandi hanno il modo di accettare comandi in modi diversi da una sessione terminale intertriggers. In genere, ogni comando supporta un modo per passarli comandi come opzioni o argomenti:

 su root who am i ssh [email protected] uname -a sh -c 'who am i; echo success' 

Molti di questi comandi accettano anche comandi su input standard:

 printf 'uname -a; who am i; uptime' | ssh [email protected] printf 'uname -a; who am i; uptime' | sh 

che ti permette anche di usare comodamente i documenti qui:

 ssh [email protected] <<'____HERE' uname -a who am i uptime ____HERE sh <<'____HERE' uname -a who am i uptime ____HERE 

Per i comandi che accettano un singolo argomento di comando, tale comando può essere sh o bash con più comandi:

 sudo sh -c 'uname -a; who am i; uptime' 

Per inciso, di solito non hai bisogno di exit esplicita perché il comando terminerà comunque quando ha eseguito lo script (sequenza di comandi) che hai inoltrato per l'esecuzione.

Aggiungendo alla risposta del triplo :

È importante ricordare che la sezione dello script formattata come un here-document per un’altra shell viene eseguita in una shell diversa con il proprio ambiente (e forse anche su una macchina diversa).

Se quel blocco del tuo script contiene l’espansione dei parametri, la sostituzione dei comandi e / o l’espansione aritmetica, allora devi usare la funzione di documento qui della shell in modo leggermente diverso, a seconda di dove vuoi che queste espansioni vengano eseguite.

1. Tutte le espansioni devono essere eseguite nell’ambito della shell genitore.

Quindi il delimitatore del documento qui deve essere non quotato .

 command < 

Esempio:

 #!/bin/bash a=0 mylogin=$(whoami) sudo sh < 

Produzione:

 a=0 mylogin=leon a=0 mylogin=leon 

2. Tutte le espansioni devono essere eseguite nell'ambito della shell secondaria.

Quindi il delimitatore del documento qui deve essere citato .

 command <<'DELIMITER' ... DELIMITER 

Esempio:

 #!/bin/bash a=0 mylogin=$(whoami) sudo sh <<'END' a=1 mylogin=$(whoami) echo a=$a echo mylogin=$mylogin END echo a=$a echo mylogin=$mylogin 

Produzione:

 a=1 mylogin=root a=0 mylogin=leon 

3. Alcune espansioni devono essere eseguite nella shell figlio, alcune - nel genitore.

Quindi il delimitatore del documento qui deve essere non quotato e si deve sfuggire a quelle espressioni di espansione che devono essere eseguite nella shell figlio .

Esempio:

 #!/bin/bash a=0 mylogin=$(whoami) sudo sh < 

Produzione:

 a=0 mylogin=root a=0 mylogin=leon 

Se si desidera una soluzione generica che funzioni per qualsiasi tipo di programma, è ansible utilizzare il comando expect .

Estratto dalla pagina di manuale:

Expect è un programma che “parla” ad altri programmi interattivi secondo uno script. Seguendo lo script, Expect sa cosa ci si può aspettare da un programma e quale dovrebbe essere la risposta corretta. Un linguaggio interpretato fornisce strutture di controllo ramificate e di alto livello per dirigere il dialogo. Inoltre, l’utente può assumere il controllo e interagire direttamente quando lo si desidera, restituendo successivamente il controllo allo script.

Ecco un esempio di utilizzo che expect :

 set timeout 60 spawn sudo su - expect "*?assword" { send "*secretpassword*\r" } send_user "I should be root now:" expect "#" { send "whoami\r" } expect "#" { send "exit\r" } send_user "Done.\n" exit 

Lo script può quindi essere avviato con un semplice comando:

 $ expect -f custom.script 

È ansible visualizzare un esempio completo nella seguente pagina: http://www.journaldev.com/1405/expect-script-example-for-ssh-and-su-login-and-running-commands

Nota: la risposta proposta da @tripleee funziona solo se l’input standard può essere letto una volta all’inizio del comando o se è stato assegnato un tty e non funzionerà con nessun programma interattivo.

Esempio di errori se usi una pipe

 echo "su whoami" |ssh remotehost --> su: must be run from a terminal echo "sudo whoami" |ssh remotehost --> sudo: no tty present and no askpass program specified 

In SSH, potresti forzare un’allocazione TTY con più parametri -t , ma quando sudo chiederà la password, fallirà.

Senza l’uso di un programma come expect qualsiasi chiamata a una funzione / programma che potrebbe ottenere informazioni da stdin farà fallire il prossimo comando:

 ssh [email protected] <<'____HERE' echo "Enter your name:" read name echo "ok." ____HERE --> The `echo "ok."` string will be passed to the "read" command