Dlaczego eksportowanie zmiennej w powłoce ssh drukuje listę eksportowanych zmiennych?

17

Rozważ to:

$ 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"

Dlaczego eksportowanie zmiennej w ramach bash -csesji uruchamianej przez ssh powoduje, że ta lista declare -xpoleceń (lista aktualnie eksportowanych zmiennych, o ile mi wiadomo)?

Uruchamianie tego samego bez tego bash -cnie robi:

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

Nie stanie się tak, jeśli nie export:

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

Przetestowałem to przez sshing z jednego komputera Ubuntu na inny (oba z uruchomionym bash 4.3.11) i na komputerze Arch, sshing do siebie, jak pokazano powyżej (wersja bash 4.4.5).

Co tu się dzieje? Dlaczego eksportowanie zmiennej wewnątrz bash -cwywołania daje taki wynik?

terdon
źródło
To nie odpowiada na pytanie, ale wynik jest wynikiem działania export. Zsh robi to samo.
Stephen Kitt,
@ StephenKitt tak, wiem export, próbuję zrozumieć, co się dzieje. Przeredaguję, aby wyjaśnić, że dzieje się to tylko podczas eksportowania.
terdon
Ach OK, przeczytałbym „listę aktualnie eksportowanych zmiennych, o ile mogę powiedzieć”, co oznacza, że ​​nie wiedziałeś, skąd pochodzi wynik.
Stephen Kitt
@StephenKitt Mam na myśli, że nie jestem pewien, czy to każda wyeksportowana zmienna lub określony podzbiór, czy co. O! Masz na myśli, że jest to wynik exportsamego uruchomienia? Tego nie zrozumiałem.
terdon
Zauważ, że foo=barnie ma go na liście.
deltab,

Odpowiedzi:

31

Po uruchomieniu polecenia sshjest ono uruchamiane przez wywołanie flagi $SHELLz -cflagą:

-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.  

Tak więc ssh remote_host "bash -c foo"będzie działać:

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

Teraz, ponieważ polecenie, które uruchamiasz ( export foo=bar) zawiera spacje i nie jest poprawnie cytowane w celu utworzenia całości, exportjest traktowane jako polecenie do uruchomienia, a reszta jest zapisywana w tablicy parametrów pozycyjnych. Oznacza to, że exportjest uruchamiany i foo=barprzekazywany do niego jako $0. Ostateczny wynik jest taki sam jak bieganie

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

Prawidłowe polecenie to:

ssh remote_host "bash -c 'export foo=bar'"
Xhienne
źródło
9

ssh łączy argumenty ze spacjami i ma powłokę logowania zdalnego użytkownika, która je interpretuje, więc w:

ssh localhost bash -c 'export foo=bar'

ssh prosi zdalną powłokę o interpretację

bash -c export foo=bar

polecenie (w praktyce, o ile pilot gospodarz jest typu Unix, będzie uruchamiać zdalnym powłoki z the-shell, -ci bash -c export foo=barjako argumenty).

Większość muszle zinterpretuje to jako wiersz polecenia, uruchamiając bashkomendę bash, -c, exporta foo=barjako argumenty (tak prowadzony export, gdy $0zawiera foo=bar), podczas gdy tylko chcesz go uruchomić go bash, -ca export foo=barjako argumenty.

W tym celu należy użyć wiersza polecenia, takiego jak:

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

(lub:

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

w tym przypadku), więc:

bash -c 'export foo=bar'

wiersz polecenia należy przekazać do zdalnej powłoki. Że wiersz poleceń będzie interpretowane przez większość muszli jak uruchomienie bashpolecenia z bash, -ca export foo=barjako argumenty. Zauważ, że za pomocą

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

nie działałby, gdyby powłoka logowania zdalnego użytkownika była rclub esna przykład, gdy "nie jest specjalnym operatorem cytowania. Pojedyncze cudzysłowy to najbardziej przenośne operatory cytowania (chociaż istnieją pewne różnice w ich interpretacji między powłokami, zobacz Jak wykonać dowolne proste polecenie za pomocą ssh bez znajomości powłoki logowania użytkownika zdalnego? Więcej na ten temat).

Stéphane Chazelas
źródło