Warum wird beim Exportieren einer Variablen in einer SSH-Shell die Liste der exportierten Variablen gedruckt?

17

Bedenken Sie:

$ ssh localhost bash -c 'export foo=bar'
terdon@localhost's password: 
declare -x DBUS_SESSION_BUS_ADDRESS="unix:path=/run/user/1000/bus"
declare -x HOME="/home/terdon"
declare -x LOGNAME="terdon"
declare -x MAIL="/var/spool/mail/terdon"
declare -x OLDPWD
declare -x PATH="/usr/bin:/bin:/usr/sbin:/sbin"
declare -x PWD="/home/terdon"
declare -x SHELL="/bin/bash"
declare -x SHLVL="2"
declare -x SSH_CLIENT="::1 55858 22"
declare -x SSH_CONNECTION="::1 55858 ::1 22"
declare -x USER="terdon"
declare -x XDG_RUNTIME_DIR="/run/user/1000"
declare -x XDG_SESSION_ID="c5"
declare -x _="/usr/bin/bash"

Warum führt das Exportieren einer Variablen innerhalb einer bash -cSitzung, die über ssh ausgeführt wird, zu dieser Liste von declare -xBefehlen (die Liste der derzeit exportierten Variablen, soweit ich das beurteilen kann)?

Dasselbe ohne das bash -causzuführen, macht das nicht:

$ ssh localhost  'export foo=bar'
terdon@localhost's password: 
$

Es passiert auch nicht, wenn wir nicht export:

$ ssh localhost bash -c 'foo=bar'
terdon@localhost's password: 
$ 

Ich habe dies getestet, indem ich von einem Ubuntu-Rechner auf einen anderen (beide mit Bash 4.3.11) und auf einen Arch-Rechner sshing, wie oben gezeigt (Bash-Version 4.4.5).

Was ist denn hier los? Warum erzeugt das Exportieren einer Variablen innerhalb eines bash -cAufrufs diese Ausgabe?

terdon
quelle
Dies beantwortet die Frage nicht, aber die Ausgabe ist das Ergebnis der Ausführung export. Zsh macht das Gleiche.
Stephen Kitt
@ StephenKitt Ja, ich weiß export, ich versuche zu verstehen, was passiert. Ich bearbeite, um zu verdeutlichen, dass dies nur beim Export geschieht.
Terdon
Ah OK, ich habe "die Liste der aktuell exportierten Variablen, soweit ich das beurteilen kann" gelesen, was bedeutet, dass Sie nicht wussten, woher die Ausgabe stammt.
Stephen Kitt
@StephenKitt Ich meine, ich bin nicht sicher, ob das jede exportierte Variable oder eine bestimmte Teilmenge ist oder was. Oh! Du meinst, es ist die Ausgabe von exportRun alone? Das hatte ich nicht verstanden.
Terdon
Beachten Sie, dass foo=barnicht in der Liste angezeigt wird.
DeltaB

Antworten:

31

Wenn Sie einen Befehl ausführen ssh, wird er ausgeführt, indem Sie $SHELLmit dem -cFlag your aufrufen :

-c    If the -c option is present, then commands are read from 
      the first non-option argument command_string.  If there  are
      arguments  after the command_string, the first argument is 
      assigned to $0 and any remaining arguments are assigned to
      the positional parameters.  

Also, ssh remote_host "bash -c foo"wird tatsächlich ausgeführt:

/bin/your_shell -c 'bash -c foo'

Da der Befehl, den Sie ausführen ( export foo=bar), Leerzeichen enthält und nicht in Anführungszeichen steht, um ein Ganzes zu bilden, exportwird der Befehl als auszuführender Befehl verwendet und der Rest wird im Array mit den Positionsparametern gespeichert. Das heißt, es exportwird ausgeführt und foo=barals übergeben $0. Das Endergebnis ist dasselbe wie beim Laufen

/bin/your_shell -c 'bash -c export'

Der richtige Befehl wäre:

ssh remote_host "bash -c 'export foo=bar'"
xhienne
quelle
9

ssh Verkettet die Argumente mit Leerzeichen und lässt sie von der Anmeldeshell des Remotebenutzers interpretieren.

ssh localhost bash -c 'export foo=bar'

ssh fragt die Remote-Shell nach der Interpretation des

bash -c export foo=bar

Befehl (in der Tat , wenn die Remote - Host ist Unix-like, wird es die Remote - Shell mit laufen the-shell, -cund bash -c export foo=barals Argument).

Die meisten Schalen werden diese Befehlszeile interpretieren als den bashLaufbefehl mit bash, -c, exportund foo=barals Argument (so liefen , exportwährend $0enthalten foo=bar) , während Sie es wollen , dass es würden laufen mit bash, -cund export foo=barals Argument.

Dafür müssten Sie eine Befehlszeile wie folgt verwenden:

ssh localhost "bash -c 'export foo=bar'"

(oder:

ssh localhost bash -c \'export foo=bar\'

für das zählt) so das:

bash -c 'export foo=bar'

Kommandozeile an die Remote-Shell übergeben werden. Das Befehlszeile würde den von den meisten Schalen interpretiert wird als Lauf bashmit dem Befehl bash, -cund export foo=barals Argument. Beachten Sie, dass mit

ssh localhost 'bash -c "export foo=bar"'

würde nicht funktionieren, wenn die Login-Shell des entfernten Benutzers wäre rcoder wenn eszum Beispiel "kein spezieller Anführungszeichenoperator vorhanden ist. Einfache Anführungszeichen sind die portabelsten Operatoren für Anführungszeichen (obwohl es einige Unterschiede in der Interpretation zwischen den Shells gibt, finden Sie weitere Informationen dazu unter So führen Sie einen beliebigen einfachen Befehl über ssh aus, ohne die Anmeldeshell des Remotebenutzers zu kennen ).

Stéphane Chazelas
quelle