Warum funktioniert der Befehl "cd" nicht über SSH?

41

Ich habe versucht, einige Dateien über SSH zu sichern, aber anstatt tardie gewünschten zu speichern , habe ich meinen Home-Ordner erhalten. Ich habe einige weitere Tests durchgeführt und es läuft darauf hinaus:

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

Was zu meiner Überraschung Dateien /rootnicht auflistet /boot. Aber wenn ich den gesamten /bin/shBefehl von einem Terminal aus ausführe, wird er ordnungsgemäß ausgeführt cdund die /bootDateien werden gedruckt .

Was passiert hier?

Ambroz Bizjak
quelle
1
Wenn dies kein Beispiel ist, warum verwenden Sie es nicht ssh root@server /bin/sh -c "ls -l /boot"?
demure
Es ist lustig, dass du gefragt hast. Ich habe das vor dem Kommentieren versucht und trotzdem keine Verzeichnisliste erhalten.
Unxnut
2
@demure, weil ich wirklich wollte tar, und tar / boot wird den Boot-Pfad in das Ergebnis, das ich nicht will, enthalten
Ambroz Bizjak

Antworten:

35

Mit ssh können Sie einen Befehl nicht genau wie bisher als eine Reihe von Argumenten angeben, die an execvp auf dem Remote-Host übergeben werden sollen. Stattdessen werden alle Argumente zu einer Zeichenfolge verkettet und über eine Remote-Shell ausgeführt. Meiner Meinung nach ist dies ein großer Konstruktionsfehler in ssh. In den meisten Fällen handelt es sich um ein gut funktionierendes Unix-Tool. Wenn jedoch ein Befehl angegeben werden soll, wird eine einzelne monolithische Zeichenfolge anstelle von argv verwendet es wurde für MSDOS oder so konzipiert!

Da ssh Ihren Befehl als einzelne Zeichenfolge an weitergibt, sh -cmüssen Sie keine eigene Zeichenfolge angeben sh -c. Wenn Sie dies tun, ist das Ergebnis

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

mit dem ursprünglichen Zitat verloren. Die durch das getrennten Befehle &&lauten also:

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

Die erste davon führt eine Shell mit dem Befehlstext "cd" und $0= aus "boot". Der Befehl "cd" wird erfolgreich ausgeführt, das $0ist irrelevant und das /bin/sh -czeigt Erfolg an, dann ls -lpassiert das.


quelle
2
Obwohl ich zustimme, ist es bedauerlich, dass sshsich das so verhält, wenn es nicht so wäre, müsste es aufgerufen werden sexec, nicht ssh. sshist die sichere Version von rsh(die sich in dieser Hinsicht gleich verhalten hat) und rlogin(wird rshaufgerufen, rloginwenn keine Befehlszeile übergeben wurde). Es ist nur bedauerlich, dass es nicht rexecso gut implementiert wird .
Stéphane Chazelas
@StephaneChazelas gab es jemals einen Befehl rexec? Soweit ich weiß, ist es nur eine C-Bibliotheksfunktion. Guter Punkt, über rsh. Ein direkter Ersatz für rsh zu sein, war der Schlüssel zu einer schnellen Einführung in den Anfängen von ssh, als bereits jede Menge Skripte rsh verwendeten.
Ich habe es definitiv in der Vergangenheit benutzt. Wenn ich jetzt sehe, muss es auf HPUX gewesen sein, da es nicht viele andere Unices zu haben scheinen, muss ich zugeben, obwohl ich den Eindruck hatte, dass es weiter verbreitet war.
Stéphane Chazelas
Vielen Dank. Dies hat wirklich geholfen, esp. da ich mit ssh tunnele. Ich musste ssh -t machine1 'ssh -t machine2 "echo \" 1 && 2 \ "" ausführen, um' 1 && 2 'als Ausgabe zu erhalten. Das hat ein paar Stunden gekostet.
Ghayes
Wenn Sie einen Befehl präzise übergeben möchten, z. B. von "$ @", können Sie dies verwenden: "`printf "%q " "$@"`"Mit bash können Sie printfeine Zeichenfolge oder Zeichenfolgen mit Shell-Escape-Zeichenfolgen in Anführungszeichen setzen.
Sam Watkins
36

Es ist ein Zitat Problem. Ssh führt den Befehl, den Sie übergeben, bereits in einer Shell aus. Wenn Sie mehrere Parameter übergeben, werden diese mit einem Leerzeichen dazwischen verkettet, um eine Zeichenfolge zu erstellen. Der Remote-Befehl, den Sie remote ausführen, lautet also /bin/sh -c cd /boot && ls -l(keine Anführungszeichen, da die Anführungszeichen in Ihrem Befehl von der lokalen Shell interpretiert wurden).

/bin/sh -c cd /bootläuft /bin/shund sagt ihm , den Befehl auszuführen cdund auch Satz $0zu /boot. Sobald dies erledigt ist, wird die übergeordnete Shell (die von gestartete sshd) ausgeführt ls -l.

Entfernen Sie in Ihrem Fall einfach das, sh -cwas völlig nutzlos ist, es sei denn, Ihre Remote-Shell (wie in /etc/passwdoder einer anderen Kennwortdatenbank angegeben) versteht diesen Befehl nicht.

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

Wenn Sie eine andere Shell aufrufen müssen, müssen Sie den Befehl remote in Anführungszeichen setzen, um ihn vor der Erweiterung durch die von aufgerufene Remote-Shell zu schützen sshd. Wenn Ihre Anmeldeshell beispielsweise ein Bindestrich ist und Sie einen bash-Befehl ausführen möchten:

ssh root@server 'bash -c "cd ~bob && ls -l"'
Gilles 'SO - hör auf böse zu sein'
quelle
8

Ich denke, es hat mehr damit zu tun, wie die Optionen von der Shell analysiert werden. Das funktioniert zum Beispiel:

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

Dies hat das gleiche Problem wie Ihr Befehl:

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

Wenn Sie den -vSchalter auf aktivieren , können sshSie sehen, was los ist:

1. Befehl:

debug1: Sende Befehl: / bin / sh -c "cd / boot && ls -l"

2. Befehl:

debug1: Sende Befehl: / bin / sh -c cd / boot && ls -l

In der Regel müssen Sie beim Senden von Befehlen sshdie Anführungszeichen und Zeilenumbrüche in Anführungszeichen besonders beachten, da die verschiedenen Ebenen sie entfernen. Auch nicht die Mühe machen, zu senden /bin/sh.

Sie können sehr nützliche Dinge tun, wenn Sie sshdie folgenden Zitate verstanden haben . Dadurch wird der Befehl auf dem Remote-Server ausgeführt, die Ergebnisse werden jedoch lokal in einer Datei auf dem System gesammelt, auf dem Sie den sshBefehl ausgeführt haben:

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

Oder hier, wo Sie ein Verzeichnis auf einem Remote-Server tarieren und es auf dem lokalen System erstellen:

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

Verweise

slm
quelle
4

Normalerweise müssen Sie nicht angeben, welche Shell verwendet werden soll. Das funktioniert:

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

Wenn Ihre Standard-Shell jedoch problematisch ist, müssen Sie lediglich den gesamten Befehl einschließlich des Shell-Aufrufs in Anführungszeichen setzen . Eines der folgenden Werke

ssh user@host '/bin/sh -c "cd /boot && pwd"'
ssh user@host "/bin/sh -c \"cd /boot && pwd\""
tylerl
quelle
Beachten Sie, dass die cd /boot && pwdArbeit in allen Shells der Hauptfamilien (Bourne, csh, rc) /bin/sh -c "cd /boot && pwd"nicht funktioniert, wenn die Remote-Shell der rcFamilie angehört, in der "es keine Besonderheiten gibt.
Stéphane Chazelas