Come può SSH funzionare con una condizione if?


8

Ho una ifdichiarazione per calcolare i file ed eliminare tutti tranne gli ultimi tre file. Ma voglio eseguire questo comando da remoto. Come posso combinare sshcon una ifcondizione?

Ho provato questo, ma senza successo.

#!/bin/bash

ssh -t  test@192.168.94.139 "cd /var/www/test.com/backup ;

if [ $(ls  | wc -l) -lt 3  ]
then
    echo "Less"
else [ $(ls -t *.tgz|awk 'NR >3'|xargs rm -f) ]
    echo "deleted"
fi"

L'errore che ho ricevuto:

ls: impossibile accedere a * .tgz: nessun file o directory


Quindi come posso personalizzarlo?
Janith,

@ user68186 Perché? Il comando è tra virgolette.
Razze di leggerezza in orbita

2
La $( )parte del comando viene eseguita dalla shell locale prima ancora che inizi il sshcomando. Questo è vero sia quando $( )è solo, sia quando è racchiuso da "s. Tuttavia, se $( )fosse all'interno di 's, non verrebbe eseguito dalla shell locale.
Kasperd,

Risposte:


3

NOTA: qui ci sono due livelli alla domanda. Uno è "Voglio eseguire un'attività non banale su un server remoto accessibile tramite SSH". L'altro è "Sto cercando di passare una stringa complessa a un comando e l'argomento finisce per essere diverso da quello che intendevo". Sto rispondendo alla domanda di basso livello senza discutere se l'approccio utilizzato è "giusto" (conveniente, non soggetto a errori, sicuro ecc.) Per risolvere quello di alto livello. Come indicato dalle altre risposte e commenti, probabilmente non lo è.

La tua riga di comando è per lo più corretta; devi solo cambiare un po 'il preventivo.

Il problema principale è che le stringhe tra virgolette vengono espanse dalla shell locale e quindi le $(...)parti verranno valutate sul sistema locale. Per passarli al sistema remoto, è necessario racchiudere lo script tra virgolette singole.

Hai anche alcune virgolette incorporate. Nella tua sceneggiatura originale, ci sono gli argomenti per le due echos; se si modifica l'offerta esterna in virgolette singole, sarà lo script awk. Questi risultati comportano l'omissione delle virgolette, il che non disturba la echos, ma rovinerà lo script awk, poiché il segno maggiore di diventerà il reindirizzamento dell'output. Quindi, dopo aver modificato le virgolette esterne in virgolette singole, modificale in virgolette doppie.

Questo è il tuo script con la quotazione corretta. Lo script potrebbe avere altri problemi, ho appena risolto la sintassi.

#!/bin/bash

ssh -t  test@192.168.94.139 'cd /var/www/test.com/backup ;

if [ $(ls  | wc -l) -lt 3  ]
then
  echo "Less"
else [ $(ls -t *.tgz|awk "NR >3"|xargs rm -f) ]
  echo "deleted"
fi'

Voglio assegnare /var/www/test.com/backupin una variabile chiamata "BACKUPDEST" e cd $BACKUPDESTpuoi dirmi come fare?
Janith,

1
Dove assegnate il valore BACKUPDEST? Se ciò accade sul lato remoto, includilo normalmente nello script. Se si desidera impostarlo localmente (ad es. Calcolarlo nello script locale o passarlo come argomento della riga di comando), è possibile modificare la prima riga in es. "cd $BACKUPDEST"' ;- la shell espande la parte tra virgolette doppie, mantiene le virgolette singole parte intatta, concatena i due e passa il risultato come ultimo argomento a ssh.
pt314,

Uh, fanno si che "cd '$BACKUPDEST'"' ;nel caso il percorso in quella variabile contiene alcune stranezze (ad esempio uno spazio). Ancora: solo dicendo che può essere fatto in questo modo, non che dovrebbe essere fatto in questo modo.
pt314,

10

Sì, puoi eseguire script complessi tramite ssh

#!/bin/bash -e

ssh_cmd="$(cat <<-EOF
    cd /var/www/test.com/backup;

    if [ \$(ls  | wc -l) -lt 3  ]; then
        echo "less"
    elif [ \$(ls -t *.tgz|awk 'NR >3'|xargs rm -f) ]; then
        echo "deleted"
    else
        echo "something else"
    fi
EOF
)"

ssh -t test@192.168.94.139 "$ssh_cmd"

Questo esempio usa un documento bash qui per generare la stringa di comando. In ogni caso, passare gli script tramite ssh è soggetto a errori, perché la quotazione e l'escape delle variabili è difficile (attenzione alla barra rovesciata prima dei comandi). Se lo script diventa troppo complesso, è meglio copiarlo tramite scpe quindi eseguirlo sull'host di destinazione.

Non ho provato a correggere il tuo script , ma ecco un esempio di come il conteggio e l'eliminazione su un host remoto potrebbero funzionare:

#!/bin/bash -e

tmp_dir="$(mktemp -d)"

ssh_cmd="$(cat <<-EOF
    cd "$tmp_dir"

    cnt_tgz=(\$(find . -type f -name "*.tgz"))
    if [[ \${#cnt_tgz[@]} -lt 3 ]]; then
        echo "less"
    else
        rm "\${cnt_tgz[@]}"
        echo "deleted"
    fi
EOF
)"

touch "$tmp_dir/1.tgz"
ssh -t localhost "$ssh_cmd"
touch "$tmp_dir/2.tgz" "$tmp_dir/3.tgz"
ssh -t localhost "$ssh_cmd"

Il ls -t *.tgznon funziona, dal momento che il globbing sta accadendo solo sul sistema locale. Utilizzando anche lsper i file di conteggio non è una buona idea, dal momento che restituisce anche voci come ., ..e le directory.


Ho degli errori. syntax error near unexpected token elif',elif [ $(ls -t |awk 'NR >3'|xargs rm -f) ]; then'
Janith

Mancava ;un'istruzione if nel pugno. Ma non ho corretto la tua sceneggiatura! Ci sono molti altri problemi, ad esempiols *.tgz
Simon Sudler,

1
"Il ls -t * .tgz non funzionerà, dato che il globbing sta avvenendo solo sul sistema locale." - accadrà sul sistema remoto se si esce correttamente. La versione originale era nuda $(...)all'interno di una stringa tra virgolette doppie, quindi la shell locale l'ha valutata. L'escape come \$(...)mostrato nel primo esempio o l'utilizzo di virgolette singole nell'intero script, impedisce alla shell locale di espanderla, quindi viene inviata al sistema remoto, viene espansa dalla shell remota e lo script funziona come inteso.
pt314,

Questo è il tipo di situazione in cui consiglio vivamente di usare virgolette singole anziché un documento qui. Anche se hai alcune variabili che vuoi inserire, è meglio usare una stringa a virgoletta singola ed printfespandere le variabili - sarà comunque più facile da gestire che cercare di sfuggire a tutte le variabili della shell. Inoltre, eviterebbe la necessità di utilizzare catsolo per acquisire il documento qui in una variabile!
Daniel Pryden,

4

Penso che tutta questa complicata roba citata sia una prova sufficiente per non usarla ma usare invece uno script. Se si desidera evitare più sshconnessioni, reindirizzare lo script all'altro host e lasciarlo eseguire lì con un comando:

File locale, ad esempio myscript.sh:

cd /var/www/test.com/backup;

if [ $(ls  | wc -l) -lt 3  ]; then
    echo "less"
elif [ $(ls -t *.tgz|awk 'NR >3'|xargs rm -f) ]; then
    echo "deleted"
else
    echo "something else"
fi

Poi:

cat myscript.sh | \
    ssh -t test@192.168.94.139 \
    "cat - > /tmp/ms.sh && sh /tmp/ms.sh && rm /tmp/ms.sh"

Oppure (evitando un uso inutile del gatto ):

ssh -t test@192.168.94.139 \
    "cat - > /tmp/ms.sh && sh /tmp/ms.sh && rm /tmp/ms.sh" \
    < myscript.sh

In questo modo lo script locale myscript.shviene reindirizzato al lato remoto dove viene reindirizzato a un file (temporaneo) /tmp/ms.sh, eseguito e infine rimosso.

Nota: non ho verificato errori nello script originale, ma volevo solo mostrare l'idea. Non è necessaria la quotazione soggetta a errori e tutti i comandi nello script vengono eseguiti sul lato remoto.


Non è nemmeno necessario salvare lo script in un file temporaneo, basta semplicemente inserirlo in una shell adatta, ad es ssh user@host bash <myscript.sh.
pt314,

2
Tieni presente, tuttavia, che il reindirizzamento funziona solo quando lo script stesso non tenta di leggere dall'input standard.
Chepner,

@ pt314 Sì, ma vedi Pipe uno script in bash?
PerlDuck,

Sì, il reindirizzamento ha i suoi limiti. Evita inoltre una serie di problemi di sicurezza associati alla scrittura (e quindi all'esecuzione) di un file temporaneo denominato in modo prevedibile. Se usi uno script temporaneo (sia per scelta che per necessità), crealo usando mktempqualcosa di altrettanto sicuro.
pt314,

3

Preferirei posizionare lo script sull'istanza remota ed eseguirlo semplicemente tramite ssh, ma ecco il mio suggerimento su come farlo nel modo desiderato:

#!/bin/bash

HOST='test@192.168.94.139'
REMOTE_PATH='/var/www/test.com/backup'

COMMAND1="ssh \"$HOST\" 'ls \"$REMOTE_PATH\" | wc -l'"
COMMAND2="ssh \"$HOST\" 'ls -t \"$REMOTE_PATH\"/*.tgz'"
COMMAND3="xargs ssh \"$HOST\" rm -rf"

if [[ $(eval "$COMMAND1") -le 3 ]]
then
    echo "Less"
else
    eval "$COMMAND2" | awk 'NR > 3' | eval "$COMMAND3" && echo "Deleted"
fi

Appunti:

  • L'espressione condizionale -ltè sostituita da -le.
  • eval - Costruisci comando concatenando argomenti.
  • Non sono sicuro che abbiamo davvero bisogno di queste citazioni extra \"all'interno $COMMAND{1..3}dell'espressione, ma decido di aggiungerle.
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.