Bash: prompt remoto interattivo


16

Ho uno script che si collega a un server remoto e controlla se è installato un pacchetto:

ssh root@server 'bash -s' < myscript.sh

myscript.sh:

OUT=`rpm -qa | grep ntpdate`
if [ "$OUT" != "" ] ; then
    echo "ntpdate already installed"
else
    yum install $1
fi

Questo esempio potrebbe essere semplificato. Ecco myscript2.shquale ha lo stesso problema:

read -p "Package is not installed. Do you want to install it (y/n)?" choise

Il mio problema è che bash non può leggere le mie risposte in modo interattivo.

Esiste un modo per eseguire lo script locale in remoto senza perdere la capacità di richiedere all'utente?


Puoi elaborare? Non è davvero chiaro cosa stai chiedendo? Un po 'di più del tuo codice sarebbe utile.
slm

1
Solo una FYI, la ragione per cui non funziona è perché stai passando il tuo script su STDIN. Quindi quando bash va a leggere da STDIN, ottiene il tuo script (o niente dal momento che ha già letto l'intero script e non è rimasto nulla).
Patrick,

1
Risorsa utile correlata a questo: backreference.org/2011/08/10/…
slm

Risposte:


22

Prova qualcosa del genere:

$ ssh -t yourserver "$(<your_script)"

Le -tforze un'allocazione tty, $(<your_script)legge l'intero file e in questi casi passa il contenuto come un argomento ssh, che sarà eseguito dal shell dell'utente remoto.

Se lo script necessita di parametri, passali dopo lo script:

$ ssh -t yourserver "$(<your_script)" arg1 arg2 ...

Funziona per me, non sono sicuro che sia universale però.


3
Questo è esattamente quello che stavo cercando, grazie!
Anthony Ananich,

Bella soluzione, ma c'è un modo per passare un argomento your script, pur mantenendo i vantaggi della sintassi che hai usato nella tua risposta? Utilizzando ssh -t yourserver "$(<your_script --some_arg)"solo i risultati in bash: --some_arg: command not found.
sampablokuper

1
@sampablokuper: tryssh ... $(<script) script_arg1 script_arg2 ...
Mat

@Mat, grazie, WFM :) Forse aggiungilo al corpo della tua risposta?
sampablokuper,

3

Il problema è che sshavvia una shell di accesso non interattiva sul computer remoto. L'ovvia soluzione semplice sarebbe quella di copiare lo script sul server remoto ed eseguirlo da lì:

scp myscript.sh root@server:/tmp && ssh root@server /tmp/myscript.sh

Se la copia non è un'opzione per qualsiasi motivo, modificherei lo script per connettermi prima e verificare se $1è installato, quindi ricollegare e installare se necessario:

OUT=$(ssh root@server rpm -qa | grep "$1");
if [ "$OUT" != "" ] ; then
    echo "$1 already installed"
else
   read -p "Package $1 is not installed. Do you want to install it (y/n)?" choice
   if [ "$choice" -eq "y" ]; then
       ssh root@server yum install "$1"
   fi
fi

Questo è ciò di cui avevo paura. La connessione a ssh è l'operazione più lunga e stavo cercando di evitarlo.
Anthony Ananich,

@AnthonyAnanich È possibile salvare il tempo di creazione della connessione impostando una connessione principale .
Gilles 'SO- smetti di essere malvagio' il

@Gilles Stavo pensando di suggerirlo, ma ho pensato che non avrebbe funzionato con il mio suggerimento. Se la prima connessione ssh funge da master, verrà chiusa dopo le $OUT=$(ssh root@server rpm -qa | grep "$1");uscite e la seconda connessione impiegherà tanto tempo quanto la prima. Ho sbagliato?
terdon

1
@terdon Esegui qualcosa come ssh -M foo.example.com sleep 99999999o ssh -M foo.example.com read <somefifocome maestro e uccidilo esplicitamente (con killo echo done >somefifo) quando hai finito.
Gilles 'SO- smetti di essere malvagio' il

0

Ecco una buona spiegazione .

Quindi ho adattato la sceneggiatura a

hostname
echo -n "Make your choice :"
read choice
echo "You typed " ${choice}
echo done

e questo non ha funzionato.

così ho spostato lo script sul telecomando per evitare il reindirizzamento locale su ssh. (I miei comandi sono in un file chiamato f )

cat f | ssh user@remotehost.com 'cat >remf'
ssh user@remotehost bash remf

Questo ha funzionato. Ecco l'output:

christian@clafujiu:~/tmp$ ssh localhost bash tmp/f
christian@localhost's password: 
Linux clafujiu 2.6.32-52-generic #114-Ubuntu SMP Wed Sep 11 19:00:15 UTC 2013 i686 GNU/Linux
Sun Nov 10 14:58:56 GMT 2013
Make your choice :abc
You typed  abc
done

Poiché @terdon ha affermato che l'intenzione originale era di eseguire gli script locali in remoto, la copia remota può essere automatizzata, questo è solo un esempio tutto su una riga.

REMID=`cat f |ssh user@remotehost 'cat > remf_$$; echo $$'` ;ssh root@redtoadservices.com "bash remf_${REMID} ; rm -v remf_${REMID}"

2
Come ha "funzionato"? Hai eseguito questo script con bash -s < script.shl'OP? Potresti includere la spiegazione nella tua risposta anziché collegarla?
terdon

Ops, mi spiace di aver dimenticato di aggiungere qualcosa di critico. Ho anche copiato lo script sul telecomando per evitare il reindirizzamento locale. Lo aggiungerò alla mia risposta per chiarirlo.
X Tian,

1
Se copi lo script sul telecomando, il problema scompare. Il problema del PO è che sta cercando di fornire input interattivi a uno script locale in esecuzione in remoto. Naturalmente verrà eseguito da te copiarlo. L'OP desidera eseguire questo script durante la connessione a un server remoto, presumibilmente a molti server remoti. Dubito che copiarlo sia pratico a meno che tu non lo automatizzi.
terdon

0

Ho cercato soluzioni a questo problema diverse volte in passato, ma non ne ho mai trovato uno soddisfacente. Il piping in ssh perde la tua interattività. Due connessioni (scp / ssh) sono più lente e il tuo file temporaneo potrebbe essere lasciato in giro. E l'intero script sulla riga di comando finisce spesso per sfuggire all'inferno.

Recentemente ho riscontrato che la dimensione del buffer della riga di comando è generalmente piuttosto grande ('getconf ARG_MAX> 2MB dove ho guardato). E questo mi ha fatto pensare a come potrei usarlo e mitigare il problema della fuga.

Il risultato è:

ssh -t <host> /bin/bash "<(echo "$(cat my_script | base64 | tr -d '\n')" | base64 --decode)" <arg1> ...

o usando un documento qui e un gatto:

ssh -t <host> /bin/bash $'<(cat<<_ | base64 --decode\n'$(cat my_script | base64)$'\n_\n)' <arg1> ...

Ho ampliato questa idea per produrre uno script di esempio BASH perfettamente funzionante in sshxgrado di eseguire script arbitrari (non solo BASH), in cui gli argomenti possono essere anche file di input locali, oltre a ssh. Vedi qui .


Interessante, ma come e / o quando è meglio della risposta di Mat ?
Scott,

1
La risposta di Mat è un buon primo passo (che uso spesso), tuttavia non è in grado di gestire una sceneggiatura di contenuto arbitrario, come i "personaggi che dovranno essere evitati. Inoltre, è limitato agli script BASH. La mia soluzione è un caso generale per qualsiasi contenuto di script di qualsiasi linguaggio di scripting installato. Inoltre, in sshx dimostrerò ulteriormente la serializzazione dei file degli argomenti, il che è piuttosto ingegnoso.
SourceSimian,

(1) Puoi pubblicare un MCVE di uno script per il quale la risposta di Mat fallisce (e la tua funziona)? (2) Vedi Cosa c'è di sbagliato in "echo $ (stuff)" o "echo` stuff` "?  Il tuo echo "$(cat my_script | base64)" | base64 --decodesembra equivalente (-ish) a cat my_script | base64 | base64 --decode, che assomiglia molto a una no-op.
Scott,

Ciao @Scott. (1): si consideri lo script: echo "ARG1=$1, USER=$USER, END". a) $ ssh host "$(<my_bash)" foo-> ARG1=, USER=user, END foo. b) $ ssh host bash "<(echo $(cat my_bash|base64) | base64 --decode)" foo-> ARG1=foo, USER=user, END. Qui (a) funziona effettivamente: bash -c $'#!/bin/bash\necho "ARG1=$1, USER=$USER, END" foo'o qualcosa di simile. Nota che l'arg non funziona. Dove (b) è in esecuzione: bash <(echo IyEvY...5EIgo= | base64 --decode) foo. (2) Ho usato base64 come codifica di trasporto.
SourceSimian,
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.