#!/bin/bash ############################################### # A flexible tool for managing remote command # # execution across multiple machines # # by # # Elliott Forney # # on # # 6.27.2008 # ############################################### ## Constants ########################### # name of this script prog_name="$(basename ${0})" # location to store temporary files tmp_dir='/tmp' # remote command program, could be ssh or rsh rcmd='ssh -o ConnectTimeout=1 -o StrictHostkeyChecking=no -o ForwardX11=no' # program usage usage="Usage: $prog_name [-h|--help] [-p] [-f host_file] [-l log_file] [-d seconds] command" ## Functions ########################### function print_help { echo -e "$usage" cat << EOM $prog_name is a flexible tool for managing remote command execution across multiple machines. command The command(s) to be executed on remote hosts. -h|--help Print this message. -f host_file A file containing a list of hosts on which command will be executed. -l log_file The file to log stderr and stdout to. -p Use parallel mode. In parallel mode, all commands will be executed simultaniously as background jobs. $prog_name will then wait for execution to complete on each host. -d seconds The integer delay in seconds to wait between executing command on successive hosts. Defaults to 0. $prog_name was written by Elliott Forney on 6.27.2008 EOM } # parse command line paramaters function parse_args { # seperate args with getopt args=$(getopt -n "$prog_name" -o +hpqf:l:m:d: -l help -- "$@") # exit on bad option if [[ $? -ne 0 ]] then echo -e "$usage" exit 1 fi # reassign command params to args eval set -- "$args" # iterate through paramaters while [[ "$1" != '--' ]] do case "$1" in # print help and exit -h|--help) print_help exit 0 ;; # set host_list file name -f) shift host_file="$1" ;; # if -l then set log_file file name -l) shift log_file="$1" ;; # set time to sleep between hosts -d) shift sleepy_time="$1" ;; # run in parallel -p) parallel=true ;; esac shift done shift # exit if no command was given if [[ "$@" == '' ]] then echo "${prog_name}: No command given!" >&2 echo -e "$usage" exit 1 fi # command to be run cmd="$@" } # run the commands sequentially function rcmd_seq { ( # redirect stderr to stdout exec 2>&1 for host in $host_list do echo $host echo '-------' $rcmd $host "$cmd" echo sleep $sleepy_time done # send to log and stdout ) | tee -i "$log_file" 2>&1 } # collect log files when run in parallel function rcmd_par_collect { # collect individual logs > "$log_file" for host in $host_list do cur_log="${tmp_dir}/${host}-${$}.tmplog" if [[ -f "$cur_log" ]] then cat "$cur_log" >> "$log_file" rm -f "$cur_log" fi done # send results to stdout if [[ -f "$log_file" ]] then cat "$log_file" fi # if no log file given if [[ "$no_log" == true ]] then # remove temp log rm -f "$log_file" fi } # run the commands in parallel function rcmd_par { # make sure we have a log if [[ "$log_file" == '/dev/null' ]] then no_log=true log_file="${prog_name}-${$}.log" fi trap 'rcmd_par_collect; exit 2' INT # loop through hosts for host in $host_list do ( # redirect stderr to stdout exec 2>&1 echo $host echo '-------' $rcmd $host "$cmd" echo # send to individual temp logs named by pid ) > "${tmp_dir}/${host}-${$}.tmplog" 2>&1 & sleep $sleepy_time done # wait for commands to finish wait rcmd_par_collect trap - INT } # echo hosts in file or read from stdin if host_file not set function build_hostlist { while read host do host_list="$host_list $host" done < <(cat $host_file) } ## Run the remote commands ########################### cmd='' # command to be executed log_file='/dev/null' # name of log file host_list='' # list of hosts to hit host_file='' # file containing host names sleepy_time=0 # time to sleep between hosts parallel=false # run commands in parallel # parse command line parameters parse_args "$@" # build host list build_hostlist # choose parallel or serial if [[ "$parallel" == true ]] then rcmd_par else rcmd_seq fi