1

I have an environment similar to the one in this question: Different shells for interactive and non-interactive work

I'm currently stuck with tcsh as my "official" default shell. For interactive shells, I essentially exec /bin/bash from my ~/.login file.

Is there any way to have bash be the shell for non-interactive shells? Ie, if I do ssh myserver env it prints out that the shell is /bin/tcsh. I was looking at ~/.cshrc and I see where I could put something in there to do this, but I don't know what to put in there. Or perhaps there is a different place to put something like this?

Currently if I exec /bin/bash from ~/.cshrc for non-interactive shells, the above ssh command will hang (presumably because it's trying to exec an interactive shell from the non-interactive one).

Is it even possible to do what I want to do?

6
  • 2
    Are you asking specifically about the shell used by ssh, or are you actually asking about non-interactive shells (i.e. scripts, which should be using the shell specified via their #!-line)?
    – Kusalananda
    Sep 17, 2021 at 5:53
  • @Kusalananda, isn't the shell started by SSH in e.g. `ssh somehost env' non-interactive too?
    – ilkkachu
    Sep 17, 2021 at 7:30
  • 1
    @ilkkachu It is (if you're running a command), but the question title as about "non-interactive shells" and the question body about "the specific shell started by ssh". These are two different things. Affecting them (which shell is started) is done through different means. SSH always runs the user's login shell. A script doesn't necessarily do that.
    – Kusalananda
    Sep 17, 2021 at 7:34
  • Must you remain with tcsh? If not, try chsh --shell /bin/bash
    – roaima
    Sep 17, 2021 at 14:48
  • Specifically, it would be the shell started by SSH. I hadn't thought about that vs all non-interactive shells. There are some great suggestions here and I'm going to give them a try and see. Sep 18, 2021 at 12:24

2 Answers 2

4

If your remote machine is Linux, try adding this to your ~/.tcshrc:

if ($?SSH_CONNECTION && $shlvl == 1) then
    set sh = /bin/bash
    set cmdline = "`tr '\0\n' '\n\1' </proc/$$/cmdline`"
    if ($#cmdline > 2) then
        exec $sh -c 'exec "$0" -c "$(printf %s "$1" | tr "\1" "\n")"' $sh $cmdline[3]:q
    endif
endif

When you call ssh user@host cmd, what the ssh server basically does is calling the login shell of user with -c and cmd passed to it as arguments. If you call just exec /bin/bash from your ~/.cshrc, bash will be called without arguments and will expect to read commands from its stdin, which means that ssh user@host cmd will appear to "hang" until you press Ctrl-C or Ctrl-D.

The kludge above tries to get the cmd argument from /proc/<pid>/cmdline and then call your choice shell (/bin/bash) in the same way, with -c and cmd passed to it as arguments. It assumes that no ascii \1 control character (Ctrl-A) is present in cmd. That and most of the complications are from trying to preserve newlines in cmd, and I'm not at all sure I got that shit right ;-)

Improvements (and simplifications!) welcome.

3
  • 2
    In csh set a="`printf 'X\n\nY'`" works differently from the bourne shell: it splits on newlines, setting $a[1] to X and $a[2] to Y. If you have a better idea how to get rid those trs and the extra exec, a testcase would be (assuming that the login shell of user is (t)csh, and the local shell supports $'...'): ssh user@host $'echo $0; cat <<EOT\na b\n\n\nc\nEOT\n' should preserve both the spaces and the newlines exactly, as if her login shell were bash.
    – user313992
    Sep 17, 2021 at 8:34
  • Ah, ok. No, I just wondered why that was required, since I'm not that familiar with csh.
    – ilkkachu
    Sep 17, 2021 at 8:40
  • Thank you Kusalananda, Uncle Billy, and Stéphane Chazelas. Once I realized the real question was about the SSH shell and with Stéphane's SSH magic plus Uncle Billy's .cshrc logic I was able to accomplish my goal. Thank you all very much. (I really wish I could accept both answers, since they were both extremely useful.) Sep 20, 2021 at 15:54
1

You could add to your ~/.ssh/authorized_keys:

command="setenv SHELL \"`which bash\"`; if ($?SSH_ORIGINAL_COMMAND) eval 'exec bash -c $SSH_ORIGINAL_COMMAND:q'; exec bash -l" your-pub-key-here

To force that command line, interpreted by your login shell (tcsh) to be run instead of the requested command when you ssh in with that key. If passed a command line, it will be available in the $SSH_ORIGINAL_COMMAND env var, and we tell bash to execute it, if not, we just start bash as a login shell.

We set $SHELL to the path of bash to tell applications started within the ssh session that bash is your preferred shell instead of tcsh your login shell.

1
  • 1
    I guess that you could just set command="" ssh-rsa ... in ~/.authorized_keys and move all that SSH_ORIGINAL_COMMAND logic to ~/.cshrc.
    – user313992
    Sep 17, 2021 at 18:50

You must log in to answer this question.

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