66

I would like to do something like ssh example.com 'ls' However per ssh manpage:

If command is specified, it is executed on the remote host instead of a login shell.

So what happens is that ls displays its output and then ssh exits.

What I can't figure out is how to have the full login shell open and then have the command run inside that shell. Leaving the shell open after the command is run. As if I had manually done the following:

  localhost$ ssh example.com 
example.com$ ls
             /folder1 
             /folder2 
example.com$ _

Any ideas?

5
  • this is a similar question superuser.com/questions/261617/… but none of the answers really seem to fit what I'm trying to do.
    – matthew
    Jul 5, 2011 at 14:16
  • How about ssh example.com 'ls;bash'? Jul 5, 2011 at 14:40
  • you need the -i on my systems to make the second shell an interactive one.
    – Flexo
    Jul 5, 2011 at 15:01
  • option -t is the answer to your question. Other options (for example keychain) exist but depends on your real needs, which are not clear enough to me .
    – hornetbzz
    Jul 5, 2011 at 23:33
  • @hornetbzz -t gives me pseudo-tty. But otherwise the behavior is the same. I want to launch an interactive shell, run a command inside that shell and have the shell remain open after the command is run.
    – matthew
    Jul 6, 2011 at 3:50

7 Answers 7

56

Just tell bash to execute ls and then itself in a login shell

$ ssh user@host  -t 'bash -l -c "ls;bash"'
1
  • 2
    Unfortunately this doesn't appear to work while using screen. Otherwise this seems like it does what I was trying to do.
    – matthew
    Jul 17, 2013 at 17:57
41
ssh user@host -t 'ls; exec $SHELL -l'

-t Force pseudo-terminal allocation. This can be used to execute arbitrary screen-based programs on a remote machine. Is slightly more proper than bash -i.

exec No new process is created.

-l looks for ~/.bash_profile, ~/.bash_login, and ~/.profile, in that order, and reads and executes commands from ... Without this you probably can not run scripts/commands from ~/bin directory, because this code from ~/.profile will not be executed without -l flag:

if [ -d "$HOME/bin" ] ; then
    PATH="$HOME/bin:$PATH"
fi
7
  • @grawity @Alan This might work as a workaround, but I'd really like the command to run inside the shell and not just open a new shell after the command is run.
    – matthew
    Jul 5, 2011 at 15:46
  • @matthew: You'll have to patch bash yourself to allow that. Jul 6, 2011 at 14:34
  • @grawity not really, see my answer below
    – fons
    Jul 17, 2013 at 15:59
  • @matthew, use exec bash or exec $SHELL Mar 1, 2016 at 8:44
  • In my case, this does not work because it opens an interactive shell, and I just need to execute a command (from a script) in a non-interactive login shell.
    – Dr_Zaszuś
    Jul 3, 2020 at 13:41
3

In your comment on fons's answer, you say that it doesn't work while using screen.

Could you elaborate on that? Looking at the source code for openssh, sshd executes the command by calling

YOUR_DEFAULT_SHELL -c COMMAND

So, for example, if your default shell is screen, then this won't work all that well because screen's -c flag just overrides its .scrreenrc. So, there's really no way to send commands to screen if it's your default shell. You'll have to actually run screen as the command given to ssh, but with a default shell that isn't screen.

If that's what you're trying to do, I think things will get really weird, since screen will also close windows with non-interactive programs, so you'll have to do a similar trick to fons's, but one level deeper. SO, with, e.g. /bin/bash (and not screen) as your default shell Something like:

ssh user@host -t 'screen bash -l -c "ls;bash"'

Which should --take a deep breath-- ssh into the host, run bash -c with a command of screen, which will make a new window. If this window just opened up ls, it would end, and screen would terminate, so we use fons's trick inside the new screen window.

I think that'll work, if that's even what you were trying to do ;)

1
  • I think the problem I have with screen in this situation, is that I normally load it with exec screen -RR from my .profile. This means that bash -l tries to load screen which throws off the rest of it. it seems I can get around it by removing '-l' in both yours and @fons solutions (yours then leaves me in screen). It's kind of wonky though.
    – matthew
    Jul 24, 2013 at 15:33
3

I use Windows and maybe because of this, the -t flag does not work for me.

To execute the ssh command with logging, I first call the files /etc/profile and ~/.profile.

In totoal, my command has the form:

ssh [email protected] 'source /etc/profile; source ~/.profile; my bash code'
1

Because I often struggle with properly escaping my command, grab this function:

sshlogin() { ssh -tt "$1" "$(printf "%q " bash -lc '"$@"' -- "${@:2}")"; };

with it not only login shell is run, but also spaces and quoting are preserved:

$ sshlogin server echo "a    space    b"
a    space    b
1

I found this thread while looking for a way to run a python command, within a miniconda environment, on a remote host.

The solution which worked for me was like this:

ssh user@host -t 'bash -i -c "<conda-command>; cd <dir>; python <script.py> <arguments>"'

Note the command passed by SSH (within the outer single quotes) is to run bash -i -c, with everything in the "", which are within the '' pair, then being the commands which that bash shell runs. Note this means any double-quoted options passed to the python script then needs to be escaped with a backslash, so --option \"cute fluffy kittens\".

This didn't work for me using bash -l, but I saw the reference to bash -i in one of the answers above, tried it, and it worked.

0

Multiple -t options force tty allocation, even if ssh has no local tty:

ssh -tt user@host 'bash -l -c "/path/to/command'

You must log in to answer this question.

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