Suppose that some variable FOO is defined (and export-ed) in the current environment. Let me call this environment E0.

Now, I log into some other Unix system using ssh.

% ssh frobozz@for.example.com

Let me call the environment of this newly established session E1.

In general, FOO will not be defined in E1, and, in the exceptional cases where it is, in general its value will be set independently of the value of FOO in E0.

How can I arrange things so that, upon log in via ssh, the variable FOO is set in E1 to the value it has in E0 (and, ideally, export-ed in E1 as well)?

In case it matters, I'm using zsh for both E0 and E1.

For the answer to this question, assume that I do not have any control over the configuration of the sshd process running on for.example.com. This rules out, in particular, any solution that requires modifying /etc/ssh/sshd_config, or the like, on for.example.com.

Also, assume that the value of FOO is not constant; it will change from one login session to another. In particular, FOO may not be set in FOO at all, in which case it is ok if the effect on E1 is analogous to that of FOO= or export FOO= or unset FOO.

UPDATE: I tried this

% ssh frobozz@for.example.com FOO=$FOO env

...and indeed, the output shows that FOO is set as specified. But I have not figured out how to apply this idea when initiating a login session. Specifically, if I run

% ssh frobozz@for.example.com FOO=$FOO

...the command just returns without establishing a connection (I figure that ssh interprets FOO=$FOO as the "command" to run on the remote host). On the other hand, after I run this

% ssh frobozz@for.example.com FOO=$FOO zsh

...I never see the shell prompt from the remote host; the command just hangs there. (I do, however, get a password prompt from ssh right before this.) The same thing happens for other variants of the last command, such as

% ssh frobozz@for.example.com FOO=$FOO /path/to/zsh
% ssh frobozz@for.example.com FOO=$FOO env zsh
% ssh frobozz@for.example.com env FOO=$FOO zsh
% ssh frobozz@for.example.com env FOO=$FOO zsh -l
% ssh frobozz@for.example.com 'env FOO=$FOO zsh'

UPDATE2: Actually, what looked like "hanging" turns out to be that the prompt is not visible. I can still run commands. Also, the sequence of zsh initialization files that gets run upon login is different from normal, and (perhaps not suprisingly) the environment is substantially (and non-trivially, IOW above and beyond FOO) different from normal.

UPDATE3: With help from dave_alcarin and meuh I found that this gets pretty close to what I am after:

% ssh -t frobozz@for.example.com env FOO=$FOO zsh -l -s

There are still some non-trivial differences between the new environment and the normal one that I need to sort out, but this is pretty close to what I want.

  • 1
    does this correspond to your situation? superuser.com/questions/48783/… – dave_alcarin Oct 14 '15 at 12:49
  • @dave_alcarin: yes, thank you. (The accepted answer in that thread does not work for me, but some of the other answers do.) – kjo Oct 14 '15 at 13:05
  • I saw that after your last edit, maybe the accepted was not your thing, but some other answers suggested other methods, which can be adapted to your dynamic setting – dave_alcarin Oct 14 '15 at 13:09
  • @dave_alcarin: I spoke too soon. See my UPDATE. – kjo Oct 14 '15 at 13:25
  • 1
    use ssh -t ... FOO=$FOO zsh to have a pty. – meuh Oct 14 '15 at 14:00

SSH has a feature to pass environment variables from the client to the server, but OpenSSH disables it in the default server configuration. There's one exception: TERM, which has a special place in the protocol; but communicating information via TERM is awkward because you have to be sure that the client will decode it.

Nevertheless some servers are configured to let some environment variables through. For example, Debian configures the SSH server to allow all variables whose name begins with LC_. Usually these are locale variables, but you can put whatever you like.

If the server isn't configured to allow any variable, you can pass them as part of the command, but there are a couple of wrinkles. The wrinkle you're hitting is that if you pass a command to ssh, then by default no terminal is created on the server. So ssh frobozz@for.example.com FOO=$FOO zsh does in fact run a shell, but that shell is connected to a pipe, not to a terminal, so it doesn't display a prompt or support command line edition. Nonetheless, you can type commands — try typing ls Enter, and exit Enter or Ctrl+D to exit. The solution is to explicitly tell SSH to open a terminal, with the -t option.

ssh -t frobozz@for.example.com FOO=$FOO zsh

Depending on what your login shell is, you may want to run

ssh -t frobozz@for.example.com FOO=$FOO exec zsh

to avoid having an extra shell process that's the parent of that explicitly-invoked zsh.

The second problem you haven't run into yet is quoting. The SSH protocol transmits a string which is executed by the remote shell. Here, you're passing the string consisting of FOO=, followed by the value of FOO, followed by a space and exec zsh. Thus the value of FOO is interpreted as a bit of shell source code, not as a string to store in the FOO variable. This makes a difference if the value contains characters that have a special meaning in shell syntax. You need to add an extra level of quoting. Assuming your local shell is zsh, you can use the q parameter expansion flag:

ssh -t frobozz@for.example.com FOO=${(q)FOO} exec zsh

Another difference with the straight case is that you're first running a login shell on the remote side, and then replacing it with a non-login shell. Since the chain starts with a login shell (this cannot be avoided), your .profile or .zprofile does get run; however the interactive instance of zsh is not a login shell, which might make a difference depending on the content of your dot files. The best solution for that is probably to avoid doing fragile things in your dot files. You can run zsh -l, but that runs a second login shell, which is prone to making more of a difference (though you can fix that, too, by making sure that your .profile or .zprofile is idempotent).

Your Answer

