Take the 2-minute tour ×

I've searched around a bit for similar questions, but other than running one command or perhaps a few command with items such as:

ssh user@host -t sudo su -

However, what if I essentially need to run a script on (let's say) 15 servers at once. Is this doable in bash? In a perfect world I need to avoid installing applications if at all possible to pull this off. For argument's sake, let's just say that I need to do the following across 10 hosts:

  1. Deploy a new Tomcat container
  2. Deploy an application in the container, and configure it
  3. Configure an Apache vhost
  4. Reload Apache

I have a script that does all of that, but it relies on me logging into all the servers, pulling a script down from a repo, and then running it. If this isn't doable in bash, what alternatives do you suggest? Do I need a bigger hammer, such as Perl (Python might be preferred since I can guarantee Python is on all boxes in a RHEL environment thanks to yum/up2date)? If anyone can point to me to any useful information it'd be greatly appreciated, especially if it's doable in bash. I'll settle for Perl or Python, but I just don't know those as well (working on that). Thanks!

share|improve this question
add comment

20 Answers

up vote 8 down vote accepted

Often, I'll just use the original Tcl version of Expect. You only need to have that on the local machine. If I'm inside a program using Perl, I do this with Net::SSH::Expect. Other languages have similar "expect" tools.

share|improve this answer
Went this route. Thanks for the tip (and the edit). Other had good ideas, but I'm looking to run a some 300 line script. Perl brings a bit of 'elegance' to that that bash won't provide. (Perl.. elegance.. never thought I'd say that) :) –  f4nt Nov 2 '08 at 16:47
If you do perl right, it can be nice ;) –  Johan Nov 9 '09 at 9:02
add comment

You can run a local script as shown by che and Yang, and/or you can use a Here document:

ssh root@server /bin/sh <<\EOF  
wget http://server/warfile    # Could use NFS here  
cp app.war /location  
command 1  
command 2  
/etc/init.d/httpd restart  
share|improve this answer
add comment

The issue of how to run commands on many servers at once came up on a Perl mailing list the other day and I'll give the same recommendation I gave there, which is to use gsh: http://outflux.net/unix/software/gsh

gsh is similar to the "for box in box1_name box2_name box3_name" solution already given but I find gsh to be more convenient. You set up a /etc/ghosts file containing your servers in groups such as web, db, RHEL4, x86_64, or whatever (man ghosts) then you use that group when you call gsh.

[pdurbin@beamish ~]$ gsh web "cat /etc/redhat-release; uname -r"
www-2.foo.com: Red Hat Enterprise Linux AS release 4 (Nahant Update 7)
www-2.foo.com: 2.6.9-78.0.1.ELsmp
www-3.foo.com: Red Hat Enterprise Linux AS release 4 (Nahant Update 7)
www-3.foo.com: 2.6.9-78.0.1.ELsmp
www-4.foo.com: Red Hat Enterprise Linux Server release 5.2 (Tikanga)
www-4.foo.com: 2.6.18-92.1.13.el5
www-5.foo.com: Red Hat Enterprise Linux Server release 5.2 (Tikanga)
www-5.foo.com: 2.6.18-92.1.13.el5
[pdurbin@beamish ~]$

You can also combine or split ghost groups, using web+db or web-RHEL4, for example.

I'll also mention that while I have never used shmux, its website contains a list of software (including gsh) that lets you run commands on many servers at once. Capistrano has already been mentioned and (from what I understand) could be on that list as well.

share|improve this answer
how to use gsh with multi line commands? –  Shimon Doodkin Dec 31 '13 at 0:43
add comment

Take a look at Expect (man expect)

I've accomplished similar tasks in the past using Expect.

share|improve this answer
add comment

To give you the structure, without actual code.

  1. Use scp to copy your install/setup script to the target box.
  2. Use ssh to invoke your script on the remote box.
share|improve this answer
That might have to work in the end. I was hoping for something a bit prettier than that. In the end, that really only cuts out the pulling down of the script from subversion, and exporting a bash script is a minimal loss of time. –  f4nt Oct 28 '08 at 15:37
You can use a single script to do all that automatically. Without having you to type all the commands, in case that wasn't clear, f4nt –  Vinko Vrsalovic Oct 28 '08 at 20:28
add comment

You can try paramiko. It's a pure-python ssh client. You can program your ssh sessions. Nothing to install on remote machines.

See this great article on how to use it.

share|improve this answer
add comment

You can pipe the local script to the remote server and execute it with one command:

ssh -t user@host 'sh' < path_to_script

This can be further automated by using public key authentication and wrapping with scripts to perform parallel execution.

share|improve this answer
add comment

Have you looked at things like Puppet or Cfengine. They can do what you want and probably much more.

share|improve this answer
add comment

Although it's a complex topic, I can highly recommend Capistrano.

share|improve this answer
add comment

You can do it the same way you did before, just script it instead of doing it manually. The following code remotes to machine named 'loca' and runs two commands there. What you need to do is simply insert commands you want to run there.

che@ovecka ~ $ ssh loca 'uname -a; echo something_else'
Linux loca #1 (blahblahblah)

Then, to iterate through all the machines, do something like:

for box in box1_name box2_name box3_name
   ssh $box 'commmands_to_run_everywhere'

In order to make this ssh thing work without entering passwords all the time, you'll need to set up key authentication. You can read about it at IBM developerworks.

share|improve this answer
add comment

You can run the same command on several servers at once with a tool like cluster ssh. The link is to a discussion of cluster ssh on the Debian package of the day blog.

share|improve this answer
add comment

Well, for step 1 and 2 isn't there a tomcat manager web interface; you could script that with curl or zsh with the libwww plug in.

For SSH you're looking to: 1) not get prompted for a password (use keys) 2) pass the command(s) on SSH's commandline, this is similar to rsh in a trusted network.

Other posts have shown you what to do, and I'd probably use sh too but I'd be tempted to use perl like ssh tomcatuser@server perl -e 'do-everything-on-one-line;' or you could do this:

either scp the_package.tbz tomcatuser@server:the_place/.
ssh tomcatuser@server /bin/sh <<\EOF
define stuff like TOMCAT_WEBAPPS=/usr/local/share/tomcat/webapps
tar xj the_package.tbz or rsync rsync://repository/the_package_place
mv $TOMCAT_WEBAPPS/old_war $TOMCAT_WEBAPPS/old_war.old
mv $THE_PLACE/new_war $TOMCAT_WEBAPPS/new_war
touch $TOMCAT_WEBAPPS/new_war [you don't normally have to restart tomcat]
mv $THE_PLACE/vhost_file $APACHE_VHOST_DIR/vhost_file
$APACHECTL restart [might need to login as apache user to move that file and restart]

share|improve this answer
add comment

You want DSH or distributed shell, which is used in clusters a lot. Here is the link: dsh

You basically have node groups (a file with lists of nodes in them) and you specify which node group you wish to run commands on then you would use dsh, like you would ssh to run commands on them.

dsh -a /path/to/some/command/or/script

It will run the command on all the machines at the same time and return the output prefixed with the hostname. The command or script has to be present on the system, so a shared NFS directory can be useful for these sorts of things.

share|improve this answer
add comment

pssh may be interesting since, unlike most solutions mentioned here, the commands are run in parallel.

(For my own use, I wrote a simpler small script very similar to GavinCattell's one, it is documented here - in french).

share|improve this answer
add comment

For those that stumble across this question, I'll include an answer that uses Fabric, which solves exactly the problem described above: Running arbitrary commands on multiple hosts over ssh.

Once fabric is installed, you'd create a fabfile.py, and implement tasks that can be run on your remote hosts. For example, a task to Reload Apache might look like this:

from fabric.api import env, run

env.hosts = ['host1@example.com', 'host2@example.com']

def reload():
    """ Reload Apache """
    run("sudo /etc/init.d/apache2 reload")

Then, on your local machine, run fab reload and the sudo /etc/init.d/apache2 reload command would get run on all the hosts specified in env.hosts.

share|improve this answer
add comment

Creates hostname ssh command of all machines accessed. by Quierati http://pastebin.com/pddEQWq2

#Use in .bashrc
#Use "HashKnownHosts no" in ~/.ssh/config or /etc/ssh/ssh_config 
# If known_hosts is encrypted and delete known_hosts

[ ! -d ~/bin ] && mkdir ~/bin
for host in `cut -d, -f1 ~/.ssh/known_hosts|cut -f1 -d " "`;
    [ ! -s ~/bin/$host ] && echo ssh $host '$*' > ~/bin/$host
[ -d ~/bin ] && chmod -R 700 ~/bin
export PATH=$PATH:~/bin 

Ex Execute:

$for i in hostname{1..10}; do $i who;done
share|improve this answer
add comment

There is a tool called FLATT (FLexible Automation and Troubleshooting Tool) that allows you to execute scripts on multiple Unix/Linux hosts with a click of a button. It is a desktop GUI app that runs on Mac and Windows but there is also a command line java client.
You can create batch jobs and reuse on multiple hosts.
Requires Java 1.6 or higher.

share|improve this answer
add comment

I'm not sure if this method will work for everything that you want, but you can try something like this:

$ cat your_script.sh | ssh your_host bash

Which will run the script (which resides locally) on the remote server.

share|improve this answer
add comment

Multixterm is cool for doing this, and allows you to easily put the 1 out of n machine back in sequence.

share|improve this answer
add comment

Here's a script to do an SSH command on multiple machines, it may help:

MACHINES="machine1 machine2 machine3 machine4 machine5 machine6"
for m in $MACHINES
        cmd="ssh $m '$*'"
        eval $cmd

You could even put this so it's in your path, call it something like sshAll, then just type sshAll command.

share|improve this answer
add comment

Your Answer


