6

I connect to my school's Linux Lab frequently to work on my programming assignments remotely. Sometimes, when there are lots of other students logged in to the server, the connection is slow, and I lose work during connection timeouts. There are several servers to choose from in the lab, and I want to be able to automatically run who as soon as the connection is made, so I can see how crowded the server is, and use another one if it's pretty full. Right now, I use a function in my .bash_aliases file to streamline the connection and password entry:

In file ~/.bash_aliases:

#!/bin/bash

# ssh to the Linux lab. Takes hostnames defined in ~/.ssh/config as 
# parameters
function sshll()
{
    if [ "$@" ]
      echo "Connecting to hostname $@";
      sshpass -f <password_file> ssh $@;
    else
      echo "Connecting to default host";
      sshpass -f <password_file> ssh <user@ipaddress>;
    fi
}

This works, so I added who to the end of the ssh commands:

In file ~/.bash_aliases:

#!/bin/bash

# ssh to the linux lab. Takes hostnames defined in ~/.ssh/config as 
# parameters
function sshll()
{
    if [ "$@" ]
      echo "Connecting to hostname $@";
      sshpass -f <password_file> ssh $@ 'who';
    else
      echo "Connecting to default host";
      sshpass -f <password_file> ssh <user@ipaddress> 'who';
    fi
}

This connects, enters my password automatically, and runs who, but then closes the connection. Is there a way I can run who automatically, without closing the connection afterward?

5
  • 1
    Possible duplicate of How to keep processes running after ending ssh session? Feb 21, 2016 at 2:42
  • 1
    This question is related to, but definitely not a duplicate of that question. I'm asking how to run who automatically upon connection without closing the connection; not how to leave it running after disconnection. That would be useless in this situation, where I just want to see some output every time I connect remotely, without having to type it every time. Feb 21, 2016 at 2:51
  • Is your goal to keep session alive to run more commands , or just hold session to view output of one command that runs long time ? In first case, if server is crowded , you can log out right away and try different one, but if not crowded, continue working. Feb 21, 2016 at 4:14
  • I want to hold the session open to run more commands. Just passing who with ssh shows the output, but kills the session. Feb 21, 2016 at 4:16
  • I see . . . . So basically an init command is what you want . . . .without editing any rc file on client Feb 21, 2016 at 4:21

6 Answers 6

6
+100

TL;DR: int_ua's answer is the way to go, simplest and effortless.

Why this works the way it does

This goes back to the basic behavior of any shell. When you login simply as ssh [email protected] you get interactive version of the shell. When you run ssh [email protected] -t "command1;command2" you basically get /bin/sh -c 'command1;command2'. The first one lets you run whatever you put into the stdin while sourcing the dot files, while second just executes those commands and exits. Essentially, there is no way to run a command before interactive use ( unless it is in one of the dot files ) or get an interactive shell from the single-shot command sh -c (or whichever shell it may be, not necessarily sh ).

Failed or incomplete ideas

My first idea I had is this : if the shell uses server's local .bashrc , could one make it use client's local file on the server ? Well,no. Not unless you copy the dot file over to remote server with scp or rsync.

Second idea that i had is the PROMPT_COMMAND. This very nice bash variable runs command each time before showing your PS1 prompt, so why not set this variable before spawining bash to PROMPT_COMMAND="who; unset PROMPT_COMMAND" so that we run it as single-shot?

Problem is this variable has to be exported to the remote server, and Gilles answer helped me to do something like this:

ssh -o SendEnv=PS1 ssh localhost -t bash

or

ssh localhost -t " PROMPT_COMMAND='who; unset PROMPT_COMMAND' bash  "

Problem ? -o option SendEnv must allow that specific variable that you are trying to send in /etc/ssh/sshd_config , and besides in either case you still get to run all that stuff with /bin/sh -c

Slight improvement to int_ua's answer

So by now we can see that ssh user@server -t 'command1;shell' pattern works the best. Except there is one minor nitpick: /bin/sh -c process is still there , so why not run bash with exec to replace that process out?

 ssh [email protected] -t 'who;exec bash'
3
  • I chose this as the answer, because (1) it improves on the other answers (if only slightly), and (2) it explains why it works, which is always more helpful than just a line of code or a what. I can't award the bounty until later today, but this answer has earned it. Feb 22, 2016 at 1:43
  • 1
    Yes, exec is a really nice improvement, thank you.
    – int_ua
    Feb 23, 2016 at 6:08
  • One with PROMPT_COMMAND seems to be only solution if you want run command in remote shell - for example use alias
    – l00k
    Oct 18, 2019 at 8:40
5

Just executing bash should be enough:

 sshpass -f <password_file> ssh <user@ipaddress> 'who; bash';
9
  • How will executing bash give me the output of who...? Feb 21, 2016 at 3:46
  • 2
    bash simply will not clear the terminal history (at least by default), so you should see all the previous lines. AFAIU these lines will not be available for some automatic handling, but it shouldn't be a problem in your case. Also, if you use plain ssh you will have to use -t key.
    – int_ua
    Feb 21, 2016 at 3:55
  • 1
    Oh, maybe you've seen the first revision of the answer where I've forgot to add the who, is that the case?
    – int_ua
    Feb 21, 2016 at 3:57
  • 1
    @MichaelHoffmann Without -t, SSH won't allocate a TTY, and interactive programs like shells, editors, etc. will behave very weirdly (or fail altogether). (You'd also want to run bash -l for .profile to take effect.)
    – muru
    Feb 21, 2016 at 4:03
  • 1
    That's pretty much what would be used with either ssh or local terminal. Basic idea is command; shell or shell -c ' command1;commad2 ; shell ` . Good answer Feb 21, 2016 at 4:11
3

This is an answer, but definitely not the answer.

I discovered that sshd (the daemon for ssh services on the remote host) will run commands in ~/.ssh/rc upon connection. I just created this file, and added who there, and now a list of users is displayed every time I make a remote connection, and I still get a login shell.

On remote host:

File ~/.ssh/rc:

#!/bin/bash

# This file is executed by sshd when a remote connection is started over ssh
who

On client:

In file ~/.bash_aliases:

#!/bin/bash

# ssh to the linux lab. Takes hostnames defined in ~/.ssh/config as 
# parameters
function sshll()
{
    if [ "$@" ]
      echo "Connecting to hostname $@";
      sshpass -f <password_file> ssh $@;
    else
      echo "Connecting to default host";
      sshpass -f <password_file> ssh <user@ipaddress>;
    fi
}

I am still interested in knowing how to do this from the client side, however, for other users with similar problems who can't use ~/.ssh/rc for lack of permissions, or whatever other reason.

3

int_ua's answer is what I'd use. Just for completeness, if you can edit your own .profile or .bashrc on the remote server, you could append this to either one:

if [[ -n $SSH_TTY ]]
then
    who
fi

SSH sets various variables, one of which is SSH_TTY - you can test for these variables to determine if you're connected via SSH. Restrictions on ~/.ssh/rc should not prevent you from using this.


I use this to set my prompt (note how I skip the hostname for local shells):

if [[ -n $SSH_TTY ]]
then
    PS1="\u@\h:\w \$ "
else
    PS1="\u:\w \$ "
fi
1

I know this is an old question, but I wonder why nobody mentioned expect, that seems to be just designed for such cases. (Of course, you need to install the expect package first).

!#/usr/bin/expect

spawn ssh user@host
expect "assword:"
send "mypassword\r"
expect "$ "
send "who\r"
interact
0

If you want ssh and execute commands without exiting:

Method 1

ssh  -t USER@SERVER "command1; command2;bash --rcfile ~user/.bashrc"

OR

Method 2

ssh -t USER@SERVER "$( cat <<'EOT'
COMMANDS
bash --rcfile ~user/.bashrc
EOT
)"

Sending variables to commands in Method One:

var1="Hello World"
var2="Salam World"
ssh  -t USER@SERVER "echo $var1; echo $var2; command3;bash --rcfile ~user/.bashrc"

Works fine

I recommend use Method One and call stored shell in server and send needed variables to that:

ssh  -t USER@SERVER "bash /path/to/server/stored/script.sh $var1 $var2 $other_var;bash --rcfile ~user/.bashrc"

You must log in to answer this question.

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