21

Currently I use Fish as my main shell on local and remote hosts.

I connect to remote hosts via ssh and sftp. I wanted to open or reuse a remote tmux whenever I connect, automatically, by default; so I added this to my ~/.ssh/config:

Host example.com
RemoteCommand tmux a; or tmux
RequestTTY yes

The problem is that now I cannot connect through sftp, nor can I run a direct command from my local CLI:

➤ ssh example.com ping localhost
Cannot execute command-line and remote command.

➤ sftp example.com
Cannot execute command-line and remote command.
Connection closed

So, my question is: How can I define a default command to be executed when opening a new interactive SSH session, but make it overridable?

5 Answers 5

15

Option 1 )

You can the option Match ( see man ssh_config )

Match Host example.com exec "test $_ = /usr/bin/ssh"
     RemoteCommand tmux a; or tmux
     RequestTTY yes

This will only differentiate difference between ssh & sftp

Option 2

You create some placeholder config for your diffe command , example :

Host tmux.example.com
  HostName example.com
  HostKeyAlias example.com
  RemoteCommand tmux a; or tmux
  RequestTTY yes

And after you can still use example.com for you sftp / ping usage .

6
  • 1st option didn't work. It seems Fish doesn't export $_. However, I can manually export a variable with set -x auto_tmux 1 and change the match test to "[ 1 -eq $auto_tmux ]"
    – Yajo
    Jun 25, 2018 at 9:04
  • 8
    Thanks, this helped me find an answer that worked for me: change the match to Match exec "[[ $(ps h o args p $PPID | wc -w) -eq 2 ]]". That exec is true when ssh is invoked as "ssh <server>" but false if invoked as, say, "ssh <server> <command>"
    – cryptarch
    Nov 14, 2018 at 6:56
  • if you have multiple hostnames nomrally space-separated like Host with.example.com multiple.example.com and you wish to use Match this way, try curly braces: Match Host {with.example.com,multiple.example.com} exec "..."
    – rymo
    Dec 3, 2019 at 23:08
  • @rymo, I wasn't able to get that working with OpenSSH 8.0p1, dated 2019-05-28. Wasn't able to find any way to match on multiple hostnames with that version, except by adding it to the exec portion: Match exec "[[ %n == @(pihole|router|athena|nas) && $(ps h o args p $PPID | wc -w) -eq 2 ]]".
    – Head Geek
    Jan 3, 2020 at 16:36
  • @cryptarch, I really like your answer, but I couldn't find a way to apply it to a Windows client. Any thoughts?
    – Autumn
    Apr 30 at 1:43
5

I'm using something more complex that deals with ssh options and space in arguments:

Match exec "POSIXLY_CORRECT=1 xargs -0 getopt -o 46AaCfGgKkMNnqsTtVvXxYyB:b:c:D:E:e:F:I:i:J:L:l:m:O:o:p:Q:R:S:W:w: --  </proc/$PPID/cmdline | perl -pe 's|.*? -- ||' | { read -r c; eval a=($c); [ ${#a[@]} -le 2 ]; }"
    RemoteCommand my-command-here
    RequestTTY yes

Also, if someone wants to use RemoteCommand for some machines (like inside "host xxxx"), the same trick can be used in reverse logic (match are evaluated after host):

Match exec "POSIXLY_CORRECT=1 xargs -0 getopt -o 46AaCfGgKkMNnqsTtVvXxYyB:b:c:D:E:e:F:I:i:J:L:l:m:O:o:p:Q:R:S:W:w: -- </proc/$PPID/cmdline | perl -pe 's|.*? -- ||' | { read -r c; eval a=($c); [ ${#a[@]} -gt 2 ]; }"
     RemoteCommand none
     RequestTTY auto

Host machine1 machine2 ...
     RemoteCommand my-command-here
     RequestTTY yes

If someone is curious, I'm currently using RemoteCommand to call screen:

RemoteCommand which screen &>/dev/null && TERM=xterm screen -q -RR "%u-%C" $SHELL -l || $SHELL -l
6
  • 1
    What's that supposed to do?
    – Yajo
    Feb 10, 2019 at 7:07
  • Parses ssh args in a space/quotes-safe manner, ignores any valid ssh opt (including opt value when required) and count if the remaining args are only two ("ssh" and "host"). It's similar to @cryptarch solution but also considering extra ssh opts. Feb 11, 2019 at 10:51
  • 2
    Ssh could save us some pain if it simply consider command in cmdline to have higher precedence than -o RemoteCommend Feb 11, 2019 at 10:59
  • This doesn't work with Fish, my main shell, as explained in the question :/
    – Yajo
    Feb 11, 2019 at 11:06
  • You can add "| tee /dev/stderr" before "perl" to check what it outputs. It might need a new kind of filter Feb 13, 2019 at 18:42
1

One option would be to make a symlink alias to the ssh command:

ln -s /usr/bin/ssh $HOME/bin/ssht

To make life easier, make sure $HOME/bin is in your $PATH.

In your ssh config:

Match Host example.com exec "test $_ = $HOME/bin/ssht"
     RemoteCommand tmux new-session -A -s mysession
     RequestTTY yes

Now, you can choose to use ssht example.com or ssh example.com for tmux and non-tmux sessions, respectively.

1

here is a solution that worked for me.

It works for bash and fish

fish doesn't support $_ as @Yajo said

Match exec "ps --pid %%self -o ppid --no-headers | xargs -r ps ww --pid | grep /usr/bin/ssh"
  RemoteCommand tmux new -A
  RequestTTY yes

I used tmux new -A because my distant server supports tmux 3.1 and the automatic attach if session exists.

I had to use %%self to escape the % and be able to use %self.

Here is how it works

You ask to select the current process with --pid %self, but you ask to display your parent pid with -o ppid, then you ask to get only the value, and discard the header with --no-headers

so now you have the pid of what is supposed to be the ssh/scp/sftp command.

now you need to know, what command ran it.

| xargs -r ps ww --pid will launch ps ww --pid YOUR_PARENT_PID

The -r state for avoiding xargs to launch ps if the previous command matched nothing.

I'm using xargs and not $(), because fish doesn't support $() but () this difference would cause my code to work either in bash or fish but not both, so I'm using xargs

then we have the ps ww --pid YOUR_PARENT_PID : ww will dump the full command line so here you can play with fgrep as I did or wc as @cryptarch did.

0

This doesn't answer directly the question. However it has become for me the fix to the root problem, so I think it deserves a post.

What I really wanted is to auto-enter tmux. Instead, I just started using byobu, which builds on top of tmux but is way more comfortable. For example, it supports this use case out of the box.

After installing byobu, just run:

byobu-enable

And you're done. Next interactive session will enter byobu automatically, no matter the shell. Non-interactive connections will work as usual.

You must log in to answer this question.

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