11

I am trying to do these in a script. I have to run some commands on a remote host. Currently, I am doing this:

ssh root@host 'bash -s' < command1
ssh root@host 'bash -s' < command2
ssh root@host 'bash -s' < command3

However, this means that I have to connect to the server repeatedly, which is increasing a lot of time between processing of the commands. I am looking for something like this:

varSession=$(ssh root@host 'bash -s')
varSeesion < command1
varSeesion < command2
varSeesion < command3

Again, I need to run these commands via a script. I have taken a look at screen but I am not sure if it can be used in a script.

  • If the sequence is unconditional -- you always do 2 and 3 even if 1 failed -- and no earlier commandfile sets or changes things that cause trouble for a later one, just concatentate the three files cat command[1-3] >allcommand and use ssh u@h bash <allcommand (note with no positional args to bash -s isn't needed) – dave_thompson_085 Nov 18 at 5:44
  • Above comment is a link to this ServerFault question: "How can I create persistent SSH connection to “stream” commands over a period of time?" – JPhi1618 Nov 18 at 19:10
  • Why not just copy a script file over, run it, and then delete it? Max of 3 connections then. Reduce it to 2 by modifying the script to delete itself just before it exits... – twalberg Nov 18 at 19:20
28

You can use a ControlMaster and ControlPersist to allow a connection to persist after the command has terminated:

When used in conjunction with ControlMaster, specifies that the master connection should remain open in the background (waiting for future client connections) after the initial client connection has been closed. If set to no, then the master connection will not be placed into the background, and will close as soon as the initial client connection is closed. If set to yes or 0, then the master connection will remain in the background indefinitely (until killed or closed via a mechanism such as the “ssh -O exit”). If set to a time in seconds, or a time in any of the formats documented in sshd_config(5), then the backgrounded master connection will automatically terminate after it has remained idle (with no client connections) for the specified time.

So, the first SSH command will setup a control file for the connection, and the other two will reuse that connection via that control file. Your ~/.ssh/config should have something like:

Host host
    User root
    ControlMaster auto
    ControlPath /tmp/ssh-control-%C
    ControlPersist 30   # or some safe timeout

And your script won't need any other changes.

  • Problem is that I don't have access to ssh_config file. I am not the administrator. – Sayantan Roychowdhury Nov 18 at 3:29
  • 5
    It's your ~/.ssh_config file. Not the server's sshd_config file. – muru Nov 18 at 3:31
  • 1
    @SayantanRoychowdhury besides, you can make ssh use another config file with the -F option (that will let it ignore the admin's /etc/ssh_config). And you can give those Control... options on the command line with -o ControlMaster=auto .... – mosvy Nov 18 at 3:48
  • 4
    @SayantanRoychowdhury then use mosvy's suggestion, which uses the same options and works the same way. Include things like that in the question, please. We can't read your mind to see if ssh in your question is actually something else. – muru Nov 18 at 4:56
  • 5
    The default client config is ~/.ssh/config (not underscore), although as @mosvy says -F overrides it and also ignores the systemwide file which is /etc/ssh/ssh_config (additional directory level) – dave_thompson_085 Nov 18 at 5:48
7

You could take a hint from a similar question on StackOverflow and use a bash Here document:

ssh root@host 'bash -s' << EOF
  command1
  command2
  command3
EOF
  • Or { echo command1; echo command2; …; } | ssh root@host 'bash -s' for another approach. +1 – kojiro Nov 19 at 12:18
  • 1
    command1 is the name of a file containing commands, not the command itself. – Barmar Nov 19 at 20:41
  • From the way the question was worded, it could be either. – Twinkles Nov 20 at 10:13
4

You can use expect script. It can automate ssh connection and run commands on the remote machine. This code should shed some light on automating the ssh connection.

you are looking for something like this. store the following code in a file foo.expect

#login to the remote machine
spawn ssh username@hostname
expect "?assword" { send "yourpassword\r"}

#execute the required commands; following demonstration uses echo command
expect "$ " {send "echo The falcon has landed\r"}
expect "$ " {send "echo The falcons have landed\r"}
expect "$ " {send "echo Will the BFR? land? \r"}

#exit from the remote machine
expect "$ " {send "exit\r"}

run it as expect foo.expect

You need expect application to run this script. It can be installed with the command apt-get install expect

This book will help you explore expect script. Happy scripting!

  • 3
    Useful; except I would suggest a passwordless login (e.g. using SSH keys); saving the password and pw auth are antipatterns. – Piskvor Nov 19 at 10:49
  • @Piskvor I strongly bias towards passwordless login too but it may not be possible in all the scenarios. Hence I wrote a script that can feed the password when required. :) – daedalus_hamlet Nov 19 at 10:52
1

You can use cat to concatenate all the files and then pipe them to ssh.

cat command1 command2 command3 | ssh root@host 'bash -s'
1

Independant file descriptor for one or more SSH connections

I addition to ControlPath and ControlPersist correct way, proposed by muru's answer, I would like to present an alternative:

Method

  1. Create a fifo you will use for ssh's outputs

    You have to create this file in a path you are confident

    mkfifo $HOME/sshfifo
    
  2. Run long-running command:

    exec 8> >(exec ssh user@host /bin/bash >$HOME/sshfifo
    exec 9<$HOME/sshfifo
    
  3. Then now you don't need anymore the fifo

    rm $HOME/sshfifo
    
  4. You are now ready to send and recieve commands from your connection:

    echo >&8 uptime
    while read -t .002 -r -u 9 answer;do echo "$answer";done
    

    Nota: 0.002 seconds may be insufficient on poor internet connections... Timeout could be useless depending on type of requests. At all this could be adapted to which kind of dialog is attented.

    And so on...

    myremote() {
        echo "$@" >&8
        while IFS= read -t .02 -r -u 9 answer;do
             echo "$answer"
        done
    }
    
  5. This will stay until you explicitely close this connection:

    exec 8>&-
    exec 9<&-
    

Further

This is not perfect. You could

  • Bind STDERR too, with a second fifo

    mkfifo $HOME/sshfifo{out,err}
    exec 8> >(exec ssh user@host /bin/bash >$HOME/sshfifoout 2>$HOME/sshfifoerr)
    exec 9<$HOME/sshfifoout
    exec 10<$HOME/sshfifoerr
    rm $HOME/sshfifo{out,err}
    
  • Use interactive bash, then reduce timeout

    mkfifo $HOME/sshfifo{out,err}
    exec 8> >(exec ssh -tT user@host /bin/bash -i >$HOME/sshfifoout 2>$HOME/sshfifoerr)
    exec 9<$HOME/sshfifoout
    exec 10<$HOME/sshfifoerr
    rm $HOME/sshfifo{out,err}
    echo >&8 'set -i;PS1="ReAdY aS BoUnD\\n"'
    while IFS= read -d '' -rn 1 -t .02 -u 9 foo;do echo -n "$foo";done
    while IFS= read -d '' -rn 1 -t .02 -u 10 foo;do echo -n "$foo";done
    

    ... then watching for /^ReAdY aS BoUnD$/ from fd/10 as marker for end of execution of last command, you could avoid timout for STDERR (-u 10), then reduce timemout for STDOUT (-u 9 -t .002).

And from there

It is even possible to open multiple connection to be used together

mkfifo $HOME/sshfifo
exec 8> >(exec ssh user1@hostA /bin/bash >$HOME/sshfifo 2>&1)
exec 9<$HOME/sshfifo
rm $HOME/sshfifo

mkfifo $HOME/sshfifo
exec 11> >(exec ssh user2@hostB /bin/bash >$HOME/sshfifo 2>&1)
exec 12<$HOME/sshfifo
rm $HOME/sshfifo

Then

tee <<<uptime /dev/fd/8 >&11
read -u  9 -t .02 ansA
read -u 12 -t .02 ansB

to send same command simultaneously to both ends...

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.