Why Exporting a Variable in SSH Shell Prints Exported Variables List

bashssh

Consider this:

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

Why does exporting a variable within a bash -c session run via ssh result in that list of declare -x commands (the list of currently exported variables, as far as I can tell)?

Running the same thing without the bash -c doesn't do that:

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

Nor does it happen if we don't export:

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

I tested this by sshing from one Ubuntu machine to another (both running bash 4.3.11) and on an Arch machine, sshing to itself as shown above (bash version 4.4.5).

What's going on here? Why does exporting a variable inside a bash -c call produce this output?

Best Answer

When you run a command through ssh, it is run by calling your $SHELL with the -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.  

So, ssh remote_host "bash -c foo" will actually run:

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

Now, because the command you are running (export foo=bar) contains spaces and is not properly quoted to form a whole, the export is taken as the command to be run and the rest are saved in the positional parameters array. This means that export is run and foo=bar is passed to it as $0. The final result is the same as running

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

The correct command would be:

ssh remote_host "bash -c 'export foo=bar'"
Related Question