0

I use a Match exec ... in my .ssh/config for some rules. (Specifically, I change my ProxyJump based on my IP address). In the man page it explains

The exec keyword executes the specified command under the user's shell. If the command returns a zero exit status then the condition is considered true. Commands containing whitespace characters must be quoted.

I use two shells with fairly different syntax (zsh and xonsh) which makes it difficult to write one exec statement that works equally well on both. Is there a way to force ssh to pass the exec command to a specific shell rather than using the user's shell?

Note that this is executed with the local shell and is not related to which shell runs on the remote server.

Edit 2021-09-09:

Here's some more details. I have the following in my ssh config:

match !exec "ifconfig | egrep -q 'inet (123\.45\.|67\.89\.)'"
    ProxyJump hop

This is bash syntax. The single quotes force raw string interpretation, so the slashes are passed to egrep.

In xonsh, this syntax is illegal due to python string quoting rules. I would have to use r'123\.' to disable quoting. Note that I can't work around this by calling a different shell inside the exec command because the string quoting still is performed by the outside shell, which may change depending on the current value of $SHELL.

10
  • 1
    Can't you use zsh -c command or whatever the equivalent is for xonsh?
    – terdon
    Aug 12, 2021 at 9:19
  • "The user's shell" means "your login shell", not the shell where you type ssh …; so the fact you use two shells doesn't matter, unless you chsh often. Or unless you want the same config to work in two machines/accounts where your login shells are indeed different. Please confirm you know it's about login shell and yet the question stands. Aug 12, 2021 at 9:34
  • @terdon Wrapping with zsh doesn't change the value of $SHELL. It does work if I explicitely set the shell, e.g. from xonsh call $SHELL=/bin/sh ssh .... However this doesn't work if ssh gets run as a subprocess, so I would have to do this for many commands (e.g. sshfs, ssshuttle, etc)
    – Quantum7
    Sep 8, 2021 at 13:02
  • 1
    Why would $SHELL be relevant? That's just your user's default login shell for the system. If you want ssh to use zsh, then just have zsh /path/to/script.zsh in the .ssh/config file. I may be missing something, of course, so it would help if you could edit your question and include specific examples.
    – terdon
    Sep 8, 2021 at 13:05
  • 1
    zsh /path/to/script.zsh first appeared in this comment from @terdon . The only thing I did with it is being persistent in bringing it to your attention. Sep 28, 2021 at 16:17

1 Answer 1

0

I have been unable to find a way to force a particular shell from within ssh config files. I believe the only option is to choose one shell and make sure that any way of launching ssh sets $SHELL correctly.

This can be achieved in a few ways. For these examples I assume that my ssh_config uses bash syntax and I want to call it from xonsh, but it should be equally possible to use any combination of incompatible shells.

Wrapper scripts

One approach would be to add a wrapper script early in the PATH which sets SHELL correctly:

~/bin/ssh:

#!/bin/bash
# Set the shell to use within .ssh/config
SHELL=/bin/bash exec /usr/bin/ssh "$@"

Then make it executable (chmod +x ~/bin/ssh).

~/.xonshrc or your calling shell's configuration file:

# Prepend to PATH
$PATH = p`~/bin` + $PATH

This would have to be done for any tool that uses .ssh/config (e.g. sshuttle, sshfs, etc).

Xonsh aliases

For my xonsh-specific solution I went with an alias function.

~/.xonshrc:

def bash_shell(cmd):
    import functools
    def alias(cmd, args):
        with __xonsh__.env.swap(SHELL="/bin/bash"):
            ![@(cmd) @(args)]
    return functools.partial(alias, cmd)
aliases |= {
    'ssh': bash_shell('ssh'),
    'sshuttle': bash_shell('sshuttle'),
    # ...
}

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .