Quoting: welche Shell liest was
Zwei Shells lesen Ihren Befehl: zuerst Ihre eigene, dann die auf dem Server. Die Anführungszeichen bestimmen, welche Shell was auflöst.
NAME=local
ssh server "echo $NAME" # local shell expands it, prints: local
ssh server 'echo $HOME $NAME' # server expands it, prints: /home/tester
# NAME does not exist on the server
ssh verbindet seine Argumente mit einzelnen Leerzeichen und sendet eine einzige Zeichenkette, Anführungszeichen, die nur Ihre lokale Shell sieht, gehen daher verloren:
$ ssh server echo "a b"
a b
$ ssh server 'echo "a b"'
a b
Dasselbe gilt für Pipes. Der erste Befehl unten führt wc auf dem Server aus, der zweite auf Ihrem Rechner:
ssh server 'ls /tmp | wc -l'
ssh server ls /tmp | wc -l
Um einen lokalen Wert mit Leerzeichen oder Anführungszeichen zu übergeben, lassen Sie bash ihn maskieren:
F="a b'c"
ssh server "printf '%s\n' $(printf %q "$F")"
Mehrere Zeilen: ein Heredoc
Setzen Sie die Heredoc-Markierung in Anführungszeichen, dann wird lokal nichts aufgelöst:
ssh server 'bash -s' <<'REMOTE'
echo "user: $(whoami)"
cd /tmp && pwd
REMOTE
Ohne Anführungszeichen um die Markierung löst Ihre lokale Shell Variablen zuerst auf. Maskieren Sie diejenigen, die für den Server gedacht sind:
V=abc
ssh server 'bash -s' <<REMOTE
echo "local var: $V, remote: \$HOSTNAME"
REMOTE
Ein lokales Skript mit Argumenten auf dem Server ausführen:
ssh server 'bash -s -- one two' < script.sh
Exit-Codes
| Befehl | Exit-Code |
|---|---|
ssh server 'exit 3' | 3 |
ssh server 'false; true' | 0, weil nur der letzte Befehl zählt |
ssh server 'set -e; false; echo no' | 1, und er stoppt bei false |
ssh server nosuchcmd | 127, Befehl auf dem Server nicht gefunden |
ssh -p 1 server true | 255, SSH konnte keine Verbindung herstellen |
Ein Remote-Befehl kann auch von sich aus mit 255 enden, 255 deutet also nur auf einen SSH-Fehler hin. Die Standardfehlerausgabe bleibt von der Standardausgabe getrennt, daher funktionieren 2>/dev/null und > file auf Ihrer Seite wie gewohnt.
Warum ssh in einer Schleife nach einem Host stoppt
ssh liest die Standardeingabe und sendet sie an den Server, so frisst es den Rest der Liste. Diese Schleife lief in unserem Test nur einmal, bei drei Zeilen in der Datei:
while read -r h; do ssh "$h" uptime; done < hosts
Mit -n liest ssh die Standardeingabe nicht. Diese Schleife lief dreimal:
while read -r h; do ssh -n "$h" uptime; done < hosts
Bevor Sie eine Schleife über Produktionsserver laufen lassen, lesen Sie Fehler auf Produktionsservern vermeiden.
Daten über die Standardeingabe senden
Ohne -n ist die Standardeingabe nützlich. Remote-Befehle können sie lesen:
printf 'hello\n' | ssh server 'tr a-z A-Z'
tar -cf - folder | ssh server 'tar -C /tmp -xf -'
Befehle, die ein Terminal brauchen
Mit einem Befehl fordert ssh kein Terminal an, deshalb gibt tty die Meldung not a tty aus, und sudo kann nicht nach einem Passwort fragen:
sudo: a terminal is required to read the password; either use the -S option to read from standard input or configure an askpass helper
Fügen Sie -t für interaktive Programme hinzu:
ssh -t server sudo systemctl restart nginx
Ist Ihre eigene Standardeingabe kein Terminal, zum Beispiel in einem Skript, gibt -t die Meldung Pseudo-terminal will not be allocated because stdin is not a terminal aus. Erzwingen Sie es mit -tt, und lesen Sie die Anleitung zu diesem Fehler.
Lange Aufgaben und Hintergrundbefehle
Ein Hintergrundjob, der die Ausgabe noch offen hält, lässt ssh warten. Der erste Befehl unten dauerte in unserem Test 2 Sekunden, der zweite kehrte sofort zurück:
ssh server 'sleep 2 &'
ssh server 'nohup sleep 2 >/dev/null 2>&1 &'
Für echte Arbeit, die die Verbindung überleben muss, nutzen Sie tmux auf dem Server. ssh -f server 'command' schickt nur Ihr lokales ssh nach der Anmeldung in den Hintergrund.
Die Umgebung unterscheidet sich von Ihrer Anmeldung
Ein Befehl über SSH läuft nicht in einer Login-Shell, Dateien wie ~/.profile werden also nicht gelesen. In unserem Test gab shopt login_shell den Wert off aus, und PATH war /usr/local/bin:/usr/bin:/bin:/usr/games. Funktioniert ein Befehl von Hand, meldet aber über SSH command not found, verwenden Sie seinen vollständigen Pfad.
Fragen
Wie führe ich sudo über SSH ohne den Terminal-Fehler aus?
Ergänzen Sie -t, etwa ssh -t server sudo systemctl restart nginx, damit sudo ein Terminal für die Passwortabfrage hat. In Skripten nutzen Sie -tt oder richten sudo für diesen Befehl ein.
Warum funktioniert ein Befehl von Hand, aber nicht über SSH?
Ein Befehl über SSH läuft in keiner Login-Shell, daher wird ~/.profile nicht gelesen und PATH ist kürzer. Verwenden Sie den vollständigen Pfad des Programms.
Wie führe ich mehrere Befehle in einem SSH-Aufruf aus?
Schreiben Sie sie in eine Zeichenkette in Anführungszeichen, etwa ssh server 'cd /tmp && ls', oder senden Sie ein Skript mit ssh server 'bash -s' < script.sh.