Pourquoi l'exportation d'une variable dans un shell ssh affiche-t-elle la liste des variables exportées?

17

Considère ceci:

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

Pourquoi l'exportation d'une variable dans une bash -csession exécutée via ssh entraîne-t-elle cette liste de declare -xcommandes (la liste des variables actuellement exportées, pour autant que je sache)?

Exécuter la même chose sans le bash -cne fait pas ça:

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

Cela ne se produit pas non plus si nous ne export:

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

J'ai testé cela en sshing d'une machine Ubuntu à une autre (exécutant bash 4.3.11) et sur une machine Arch, en sshing comme indiqué ci-dessus (bash version 4.4.5).

Que se passe t-il ici? Pourquoi l'exportation d'une variable à l'intérieur d'un bash -cappel produit-elle cette sortie?

terdon
la source
Cela ne répond pas à la question, mais la sortie est le résultat de l'exécution export. Zsh fait la même chose.
Stephen Kitt
@StephenKitt oui, je sais que c'est export, j'essaie de comprendre ce qui se passe. Je vais modifier pour préciser que cela ne se produit que lors de l'exportation.
terdon
Ah OK, j'avais lu "la liste des variables actuellement exportées, pour autant que je sache" comme signifiant que vous ne saviez pas d'où venait la sortie.
Stephen Kitt
@StephenKitt Je veux dire que je ne sais pas si c'est chaque variable exportée ou un sous-ensemble spécifique ou quoi. Oh! Vous voulez dire que c'est la sortie de l' exportexécution seule? Que je n'avais pas compris.
terdon
Notez que cela foo=barn'apparaît pas dans la liste.
deltab

Réponses:

31

Lorsque vous exécutez une commande ssh, elle est exécutée en appelant votre $SHELLavec l' -cindicateur:

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

Donc, ssh remote_host "bash -c foo"va réellement fonctionner:

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

Maintenant, parce que la commande que vous exécutez ( export foo=bar) contient des espaces et n'est pas correctement citée pour former un tout, le exportest considéré comme la commande à exécuter et le reste est enregistré dans le tableau des paramètres de position. Cela signifie qu'il exportest exécuté et lui foo=barest transmis sous la forme $0. Le résultat final est le même que la course

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

La commande correcte serait:

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

ssh concatène les arguments avec des espaces et fait interpréter le shell de connexion de l'utilisateur distant, donc dans:

ssh localhost bash -c 'export foo=bar'

ssh demande au shell distant d'interpréter la

bash -c export foo=bar

commande (en effet, si l'hôte distant est de type Unix, il déborde de la coque à distance avec the-shell, -cet bash -c export foo=barcomme arguments).

La plupart des obus interpréteront cette ligne de commande en cours d' exécution de la bashcommande avec bash, -c, exportet foo=barcomme arguments (donc courir exporten $0contient foo=bar) pendant que vous voudriez l'exécuter avec bash, -cet export foo=barcomme arguments.

Pour cela, vous devez utiliser une ligne de commande comme:

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

(ou:

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

pour cela compte) donc:

bash -c 'export foo=bar'

la ligne de commande soit passée au shell distant. Cette ligne de commande serait interprétée par la plupart des shells que l' exécution de la bashcommande avec bash, -cet export foo=barcomme arguments. Notez que l'utilisation

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

ne fonctionnerait pas si le shell de connexion de l'utilisateur distant était rcou espar exemple où "n'était pas un opérateur de devis spécial. Les guillemets simples sont les opérateurs de devis les plus portables (bien qu'il existe des variations sur la façon dont ils sont interprétés entre les shells, voir Comment exécuter une commande simple arbitraire sur ssh sans connaître le shell de connexion de l'utilisateur distant? Pour plus d'informations).

Stéphane Chazelas
la source