#!/bin/bash # -*-shell-script-*- # Executes a command in parallel or serial on a cluster. # Copyright (C) 2002 Ernest N. Mamikonyan # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # echo always prints to stderr echo() { builtin echo "$@" >&2; } usage() { if [[ "$0" = *cp ]]; then declare -r help_string='Copy files throughout a cluster.' else declare -r help_string='Executes a command on a cluster.' fi echo "\ Usage: ${0##*/} [OPTIONS] [COMMAND] $help_string Defaults are shown in parentheses. -f FILE specify an alternative nodes file (\`$nodes_file') -h display this help message and exit -S execute in serial -s use ssh/scp instead of rsh/rcp -V display version information and exit " exit $1 } # some initializations shopt -s expand_aliases if [[ "$0" = *cp ]]; then alias rsh_command='rcp "$@" "$node:$dest"' else alias rsh_command='rsh "$node" "$@"' fi nodes_file=/etc/nodelist serial=false version=false # parse the argument list while getopts :f:hSsV option; do case $option in f) nodes_file="$OPTARG";; h) usage 0;; S) serial=true;; s) [[ "$0" = *cp ]] && alias rcp=scp || alias rsh=ssh;; V) version=true;; :) echo "The \`-$OPTARG' option requires an argument!"; usage 1;; *) echo "\`-$OPTARG' is not a valid option!"; usage 1;; esac done shift $((OPTIND-1)) # read in the list of nodes if [[ -f "$nodes_file" ]]; then declare -ar nodes=($(< "$nodes_file")) else echo ${0##*/}: please define the list of nodes and try again usage 1 fi if $version; then echo -n "\ psh/pcp version 1.1, Copyright © 2002 Ernest N. Mamikonyan. psh comes with ABSOLUTELY NO WARRANTY. This is free software, and you are welcome to redistribute it under certain conditions; see the source for details. The following " if ((${#nodes[@]} > 1)); then echo -n ${#nodes[@]} nodes are else echo -n node is fi echo ' defined:' while ((i < ${#nodes[@]})); do echo "$((++i)): ${nodes[i-1]}"; done exit fi if [[ "$0" = *cp && $# -gt 1 ]]; then # save the last argument (the destination) eval dest="\${$#}" # make . mean PWD [[ "$dest" = .{,.}{,/*} ]] && dest="$PWD/$dest" # copy the rest into a temp array for ((i=1; i<$#; ++i)); do eval args[i]="\${$i}"; done # now, clear the whole list and restore everything but the last argument shift $#; set -- "${args[@]}" fi if $serial; then [[ "$0" = *cp ]] && message='Copying to' || message='Executing on' for node in "${nodes[@]}"; do echo -en "$message $node: \e[K" error=$(rsh_command 2>&1) [[ "$error" ]] && echo "$error" || echo -n $'done\r' done echo else trap 'rm -f "${temp_files[@]}"' EXIT INT TERM for node in "${nodes[@]}"; do log=$(mktemp "/tmp/${0##*/}.XXXXXX") || exit 1 temp_files=("${temp_files[@]}" "$log") rsh_command &> "$log" & done wait for ((i=0; i<${#temp_files[@]}; ++i)); do error=$(< "${temp_files[i]}") [[ "$error" ]] && echo "${nodes[i]}: $error" done fi