Por que a exportação de uma variável em um shell ssh imprime a lista de variáveis exportadas?

17

Considere isso:

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

Por que a exportação de uma variável dentro de uma sessão bash -c executada via ssh resulta na lista de comandos declare -x (a lista de variáveis atualmente exportadas, até onde eu sei)?

A execução da mesma coisa sem o bash -c não faz isso:

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

Nem acontecerá se não export :

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

Eu testei isso conectando de uma máquina Ubuntu a outra (ambas rodando o bash 4.3.11) e em uma máquina Arch, conectando a si mesmo como mostrado acima (bash versão 4.4.5).

O que está acontecendo aqui? Por que a exportação de uma variável dentro de uma chamada bash -c produz essa saída?

    
por terdon 10.01.2017 / 14:26

2 respostas

31

Quando você executa um comando através de ssh , ele é executado chamando seu $SHELL com o -c flag:

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

Então, ssh remote_host "bash -c foo" será executado:

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

Agora, como o comando que você está executando ( export foo=bar ) contém espaços e não está corretamente cotado para formar um todo, o export é considerado como o comando a ser executado e o restante é salvo na matriz de parâmetros posicionais . Isso significa que export é executado e foo=bar é passado para ele como $0 . O resultado final é o mesmo que correr

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

O comando correto seria:

ssh remote_host "bash -c 'export foo=bar'"
    
por 10.01.2017 / 14:41
9

ssh concatena os argumentos com espaços e tem o shell de login do usuário remoto para interpretá-lo, portanto:

ssh localhost bash -c 'export foo=bar'

ssh está pedindo ao shell remoto para interpretar o

bash -c export foo=bar

comando (na verdade, se o host remoto for do tipo Unix, ele executará o shell remoto com the-shell , -c e bash -c export foo=bar como argumentos).

A maioria dos shells interpretará essa linha de comando executando o comando bash com bash , -c , export e foo=bar como argumentos (portanto, execute export while $0 contains foo=bar ) enquanto você deseja que ele seja executado com bash , -c e export foo=bar como argumentos.

Para isso, você precisa usar uma linha de comando como:

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

(ou:

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

para isso importa, então o:

bash -c 'export foo=bar'

linha de comando é passada para o shell remoto. Essa linha de comando seria interpretada pela maioria dos shells como executando o comando bash com bash , -c e export foo=bar como argumentos. Note que usando

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

não funcionaria se o shell de login do usuário remoto fosse rc ou es , por exemplo, onde " não é um operador de cotação especial. Aspas simples são os operadores de cotação mais portáteis (embora haja alguma variação em como eles são interpretados entre os shells, consulte Como executar um comando simples arbitrário sobre o ssh sem saber o shell de login do usuário remoto? para saber mais sobre isso).

    
por 10.01.2017 / 16:35

Tags