Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems. Join them; it only takes a minute:

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

I need to be able to locally run a script that will connect to various servers and run commands on them. What is the best way to accomplish this?

share|improve this question

Personally, I would use Capistrano. It's friendly and written in Ruby and they already did all of the heavy lifting for you.

From Wikipedia:

Capistrano is a utility and framework for executing commands in parallel on multiple remote machines, via SSH.

share|improve this answer

you can run a command using ssh hostname command. If you have an entire script you need to execute, first use scp to transfer it to the remote host, then ssh to execute it.

scp script.sh host:script.sh
ssh host ./script.sh
share|improve this answer

What about using configuration management like puppet or chef? This is maybe a little over the top for only one script, but if you need several such scripts it might be worth to consider.

share|improve this answer
I think cfengine falls in this class too – xenoterracide Aug 17 '10 at 18:38

I would use ssh with key authentication, I believe ssh has a way to make sure that certain accounts can only log in from certain IP's so I would limit it to that because you might not want to set a passphrase on the keys (you could use a key manager to avoid that but it has limitations too)

share|improve this answer
Key based authentication, sure. However writing a script that starts with ssh user@host leaves me dropped into the host, blocking execution of the script. I need to remotely execute code. – Josh K Aug 17 '10 at 17:10
@Josh The next argument to ssh is the command, so ssh user@host ls / will list the root directories on that host. See Keith's answer – Michael Mrozek Aug 17 '10 at 17:56

A quickie bash 'for' loop might be easiest, perhaps something like:

for h in host1 host2 host3
   echo $h
   ssh user@$h "ps -ef"

Of course, cfengine/puppet/chef/capistrano are better configuration management options. If you wanted to interactively send commands to the various shells, clusterm (http://sourceforge.net/projects/clusterm/) is a solid choice too.

share|improve this answer

Puppet and Chef are "pull" systems and I've found that a complementary "push" system implemented using Capistrano, Fabric, or ssh(1) in a for-loop is necessary. Of course, that means public keys in place for authentication, too; fortunately, those can be managed by Puppet or Chef.

share|improve this answer

I have been pretty happy with a shell script called dssh.sh that utilizes ssh to communicate with many machines simultaneously. It can execute the same command across lots of machines simultaneously and wait for them all to exit before returning. To download and learn more about it, the best reference I have found is the BASH Cures Cancer blog.

share|improve this answer

I was having the exact same question a year ago, and after asking some people I started with Fabric. It was recommended to me over Puppet and Chef for it's simplicity.

You can easily mix local commands and remote commands in the same function. Also, being able to use Python instead of Bash is a notable upgrade that will help if you need to do lots of helper code besides the local/remote commands. Even if sometimes you need a trick to make simple things work, I still recommend Fabric.

Here's a nice introduction to Fabric: http://www.slideshare.net/panopticdev/fabric-a-capistrano-alternative The author claims that Fabric is better than Capistrano for simple systems.

Here I'll give you a small real-life example: transferring latest git repo code (stored in code_dir) to a remote machine and compiling:

# Use: fab -H <username>@<test_computer_ip> compile_on_test_computer
def compile_on_test_computer():
  # create a remote dir for incoming updates
  run("mkdir -p %s/update" % env.remoteDir)
  # create a local dir for outgoing updates
  lrun("mkdir -p %s/update" % env.localDir)

  # archive latest code, be it committed or not
  with lcd("%s/code_dir" % env.localDir):
    lrun("uploadStash=`git stash create` && git archive -o ../update/code_outgoing.zip $uploadStash || git archive -o ../update/code_outgoing.zip HEAD")

  # transfer source code to test computer, unzip and compile
  put("%s/update/code_outgoing.zip" % (env.localDir), 
  "%s/update/code_incoming.zip" % (env.remoteDir),
  with cd("%s" % env.remoteDir):
    run("unzip -uo update/code_incoming.zip -d code_dir")
    run("cd code_dir && ./compile_and_install_all.sh")
share|improve this answer

Your Answer


By posting your answer, you agree to the privacy policy and terms of service.

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