Perché il comando `cd` non funziona tramite SSH?


41

Stavo cercando di eseguire il backup di alcuni file tramite SSH ma invece di tarquelli che volevo ho ottenuto la mia cartella home. Ho fatto ulteriori test e si riduce a questo:

ssh root@server /bin/sh -c "cd /boot && ls -l"

Che per i miei file liste a sorpresa nel /rootnon /boot. Ma se eseguo l'intero /bin/shcomando da un terminale, esso viene correttamente cde stampa i /bootfile.

Cosa sta succedendo qui?


1
A meno che questo non sia un esempio, perché non lo usi ssh root@server /bin/sh -c "ls -l /boot"?
esca il

È divertente, hai chiesto. Ho provato a farlo prima di commentare e non ho ancora ottenuto l'elenco delle directory.
Unxnut,

2
@demure perché volevo davvero tar, e tar / boot includerà il percorso di avvio nel risultato, che non voglio
Ambroz Bizjak,

Risposte:


35

ssh non ti consente di specificare un comando esattamente, come hai fatto, come una serie di argomenti da passare a execvp sull'host remoto. Invece concatena tutti gli argomenti in una stringa e li esegue attraverso una shell remota. Questo si distingue come un grande difetto di progettazione in ssh secondo me ... è uno strumento unix ben educato in molti modi, ma quando arriva il momento di specificare un comando ha scelto di utilizzare una singola stringa monolitica invece di un argv, come è stato progettato per MSDOS o qualcosa del genere!

Poiché ssh passerà il tuo comando come singola stringa a sh -c, non è necessario fornire il tuo sh -c. Quando lo fai, il risultato è

sh -c '/bin/sh -c cd /boot && ls -l'

con la citazione originale persa. Quindi i comandi separati da &&sono:

`/bin/sh -c cd /boot`
`ls -l`

Il primo di questi esegue una shell con il testo del comando "cd" e $0= "boot". Il comando "cd" viene completato correttamente, l' $0irrilevante e l' /bin/sh -cindicazione riuscita, quindi ls -laccade.


2
Mentre sono d'accordo è sfortunato che si sshcomporti così, se non lo fosse, dovrebbe essere chiamato sexec, no ssh. sshè la versione sicura di rsh(che si è comportato allo stesso modo a tale riguardo) e rlogin( rshchiamata rloginquando non ha superato una riga di comando). È sfortunato che non si attui rexecanche.
Stéphane Chazelas,

@StephaneChazelas c'è mai stato un comando rexec? Per quanto ne so, è solo una funzione di libreria C. Un buon punto, però, riguardo a rsh. Essere un sostituto drop-in per rsh era la chiave per una rapida adozione nei primi giorni di ssh, quando tutti avevano tonnellate di script che già utilizzavano rsh.

L'ho sicuramente usato in passato. Guardando ora, deve essere stato su HPUX poiché non molti altri Unices sembrano averlo, devo ammettere che avevo l'impressione che fosse più diffuso.
Stéphane Chazelas,

Grazie. Questo ha davvero aiutato, esp. dal momento che sto tunneling con SSH. Ho dovuto fare ssh -t machine1 'ssh -t machine2 "echo \" 1 && 2 \ ""' per ottenere '1 && 2' come output. Quello ha ucciso alcune ore.
Ghayes,

Se vuoi passare un comando con precisione, ad es. Da "$ @", puoi usare questo: "`printf "%q " "$@"`"con bash printfper citare una stringa o stringhe con escape di shell.
Sam Watkins,

36

È un problema di citazioni. Ssh esegue già il comando per passarlo in una shell. Quando si passano più parametri, vengono concatenati con uno spazio in mezzo per creare una stringa. Quindi il comando remoto che stai eseguendo in remoto è /bin/sh -c cd /boot && ls -l(senza virgolette, perché le virgolette nel tuo comando sono state interpretate dalla shell locale).

/bin/sh -c cd /bootviene eseguito /bin/she gli dice di eseguire il comando cde anche di impostare $0su /boot. Fatto ciò, viene eseguita la shell padre (quella avviata da sshd) ls -l.

Nel tuo caso, rimuovi semplicemente ciò sh -cche è completamente inutile a meno che la tua shell remota (come indicato in /etc/passwdo altro database di password) non capisca questo comando.

ssh root@server "cd /boot && ls -l"

Se è necessario invocare una shell diversa, è necessario citare il comando remoto per proteggerlo dall'espansione della shell remota richiamata da sshd. Ad esempio, se la shell di accesso è trattata e si desidera eseguire un comando bash:

ssh root@server 'bash -c "cd ~bob && ls -l"'

8

Penso che abbia più a che fare con il modo in cui le opzioni vengono analizzate dalla shell. Ad esempio, funziona:

$ ssh root@server /bin/sh -c '"cd /boot && ls -l"'

Questo ha lo stesso problema del tuo comando:

$ ssh root@server /bin/sh -c 'cd /boot && ls -l'

Se abiliti il -vpassaggio a sshpuoi vedere cosa sta succedendo:

1o comando:

debug1: Invio comando: / bin / sh -c "cd / boot && ls -l"

2o comando:

debug1: comando di invio: / bin / sh -c cd / boot && ls -l

In genere quando si inviano comandi tramite sshè necessario prestare particolare attenzione alla quotazione e racchiudere le virgolette tra virgolette mentre i vari livelli le eliminano. Inoltre, non preoccuparti di inviare /bin/sh.

Puoi fare cose molto utili dopo aver compreso la citazione di sshquanto segue. Questo eseguirà il comando sul server remoto ma raccoglierà i risultati in un file localmente sul sistema in cui è stato eseguito il sshcomando:

$ ssh root@server 'free -m' > /tmp/memory.status

o questo, dove tar una directory su un server remoto e la crei sul sistema locale:

$ ssh remotehost 'tar zcvf - SOURCEDIR' | cat > DESTFILE.tar.gz

Riferimenti


4

In genere non è necessario specificare quale shell utilizzare. Questo funziona:

ssh user@host "cd /boot && pwd"

Ma se la tua shell predefinita è problematica, assicurati di citare l' intero comando, inclusa l'invocazione della shell. Una delle seguenti opere

ssh user@host '/bin/sh -c "cd /boot && pwd"'
ssh user@host "/bin/sh -c \"cd /boot && pwd\""

Si noti che mentre cd /boot && pwdfunziona in tutte le shell delle famiglie principali (Bourne, csh, rc), /bin/sh -c "cd /boot && pwd"non funzionerebbe se la shell remota appartiene alla rcfamiglia dove "non è speciale.
Stéphane Chazelas,
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.