bash script for executing commands on multiple server
Parse your input parameters
# create an array of server:services
a=($(echo "$1" | gawk 'BEGIN { FS="[]],[[]" } ; { print $1, $2, $3 }' | tr -d '[]'))
# add a for loop here to iterate values in array with code below
for var in "${a[@]}" ; do
# get server name
server1=$(echo $var | cut -d ':' -f1)
# get your services as space separated
servs1="$(echo $var | cut -d ':' -f2 | tr ',' ' ')"
# loop your services
for s in $servs1; do
ssh $server1 "service $s restart"
done
done
If you like bash programming or have to learn it this is the 'bible' to me
Advanced Bash-Scripting Guide
An in-depth exploration of the art of shell scripting
Mendel Cooper
http://www.tldp.org/LDP/abs/html/
How to run remote script on multiple host simultaneously
The solution could be to deploy a monitoring software with custom checks.
For the parrallel ssh
problem, without install any binaries you could use this script I wrote a while ago.
Put in a file mssh
, run chmod u+x mssh
and then :
./mssh -s SERVER1 -s SERVER2 -C script.sh
The mssh
file :
#!/usr/bin/env bash
readonly prog_name="$(basename "$0")"
readonly date="$(date +%Y%m%d_%H%M%S)"
# print help
usage() {
cat <<- EOF
usage: $prog_name options
parallel ssh executions.
OPTIONS:
-c --cmd CMD execute command CMD
-s --host SRV execute cmd on server SRV
-C --cmd CMD_FILE execute command contained in CMD_FILE
-S --hosts-file SRV_FILE execute cmd on all servers contained in SRV_FILE
-h --help show this help
Examples:
Run CMD on SERVER1 and SERVER2:
./$prog_name -s SERVER1 -s SERVER2 -c "CMD"
EOF
}
# test if an element is in an array
is_element(){
local search=$1; shift;
for e in "$@"; do [[ "$e" == "$search" ]] && return 0; done
return 1
}
# parse arguments
for arg in "$@"; do
case "$arg" in
--help) args+=( -h );;
--host) args+=( -s );;
--hosts-file) args+=( -S );;
--cmd) args+=( -c );;
--cmd-file) args+=( -C );;
*) args+=("$arg");;
esac
done
set -- "${args[@]}"
while getopts "hs:S:c:C:" OPTION; do
case $OPTION in
h) usage; exit 0;;
s) servers_array+=("$OPTARG");;
S) while read -r L; do servers_array+=("$L"); done < <( grep -vE "^ *(#|$)" "$OPTARG");;
c) cmd="$OPTARG";;
C) cmd="$(< "$OPTARG")"; file=$OPTARG;;
*) :;;
esac
done
if [[ -z ${servers_array[0]} ]] || [[ -z $cmd ]]; then
usage; exit 1
fi
# clean up created files at exit
trap "rm -f /tmp/pssh*$date" EXIT
[[ -n $file ]] && echo "executing command file : $file" || echo "executing command : $cmd"
# run cmd on each server
for i in "${!servers_array[@]}"; do
# executing cmd in subshell
ssh -n "${servers_array[$i]}" "$cmd" > "/tmp/pssh_${i}_${servers_array[$i]}_${date}" 2>&1 &
pid=$!
pids_array+=("$pid")
echo "${servers_array[$i]} - $pid"
done
# for each pid, set state to running
ps_state_array=( $(for i in "${!servers_array[@]}"; do echo "running"; done) )
echo "waiting for results..."
echo
# begin finished verifications
continue=true; attempt=0
while $continue; do
# foreach ps
for i in "${!pids_array[@]}"; do
# if already finished skip
[[ ${ps_state_array[$i]} == "finished" ]] && continue
# else check if finished
ps -o pid "${pids_array[$i]}" > /dev/null 2>&1 && ps_finished=false || ps_finished=true
if $ps_finished; then
ps_state_array[$i]="finished"
echo -e "[ ${servers_array[$i]} @ $(date +%H:%M:%S) ]" | grep '.*' --color=always
cat "/tmp/pssh_${i}_${servers_array[$i]}_${date}"
rm -f "/tmp/pssh_${i}_${servers_array[$i]}_${date}"
echo
fi
done
is_element "running" "${ps_state_array[@]}" || continue=false
if $continue; then
(( attempt < 5 )) && attempt=$(( attempt + 1 ))
sleep $attempt
fi
done
exit 0
What is the cleanest way to ssh and run multiple commands in Bash?
How about a Bash Here Document:
ssh otherhost << EOF
ls some_folder;
./someaction.sh 'some params'
pwd
./some_other_action 'other params'
EOF
To avoid the problems mentioned by @Globalz in the comments, you may be able to (depending what you're doing on the remote site) get away with replacing the first line with
ssh otherhost /bin/bash << EOF
Note that you can do variable substitution in the Here document, but you may have to deal with quoting issues. For instance, if you quote the "limit string" (ie. EOF
in the above), then you can't do variable substitutions. But without quoting the limit string, variables are substituted. For example, if you have defined $NAME
above in your shell script, you could do
ssh otherhost /bin/bash << EOF
touch "/tmp/${NAME}"
EOF
and it would create a file on the destination otherhost
with the name of whatever you'd assigned to $NAME
. Other rules about shell script quoting also apply, but are too complicated to go into here.
shell script to run commands over SSH on multiple remote servers
Thanks to @GordonDavisson recommendation.
The mutt
in the script is treating the remaining server details as an input list, hence the script is getting terminated after reading the first server values.
Replace mutt
with another email program in your script.
However, users who wish to go with mutt
, then it is recommended to add content to their email body or redirect the stdin from mutt to /dev/null
Below is the working solution:
#!bin/bash
while IFS="|" read -r userName hostIp Port Proxy proxyPort
do
STATUS=$(ssh -n -tt -o LogLevel=quiet ${userName}@${hostIp} -p${Port} 'timeout 4 /bin/bash -c' \'"</dev/tcp/${Proxy}/${proxyPort}"\'; echo $? < /dev/null | tr -d '\r')
if [ "$STATUS" -ne 0 ]
then
echo "Connection to $Proxy on port $proxyPort failed" | mutt -s "${hostIp}:Telnet connection to $Proxy on port $proxyPort failed" abc.def@xyz.com
else
echo "Connection to $Proxy on port $proxyPort succeeded" | mutt -s "${hostIp}:Telnet connection to $Proxy on port $proxyPort succeeded" abc.def@xyz.com
fi
done<.hostdetails
The trim command in the solution is to delete the carriage return(\r) I was getting in the variable(STATUS)
Run command multiple linux server
You can run ssh with the -t
flag to open a ssh session, run a command and then close the session. But to get this fully automated you should automate the login process to every server so that you don't need to type the password for every server.
So to run df -h
on a remote server and then close the session you would run ssh -t root@server.com "df -h"
. Then you can process that output however you want.
One way of automating this could be to write a bash script that runs this command for every server and process the output to check the health of the server.
For further information about the -t
flag or how you can automate the login process for ssh.
https://www.novell.com/coolsolutions/tip/16747.html
https://serverfault.com/questions/241588/how-to-automate-ssh-login-with-password
Log in to another server and run commands - Using a script
Use a heredoc.
ssh username@server02.com << EOF
echo "In server02"
EOF
Related Topics
Hide Information During Bash Debug Run
Start Tomcat from Eclipse in Port 80 in Ubuntu with Authbind
Linux Shared Library Depends on Symbols in Another Shared Library Opened by Dlopen with Rtld_Local
Phony Targets for Parallel Execution of Make
How to Overwrite Linux System Files into The Yocto Filesystem
Restart Service from Cgi Script
Setting an Acpi Field in Linux
Having an Issue Passing Variables to Subshell
Testing - Intentionally Corrupt a .Z File Using 'Dd'
Can't Run Dmidecode on Docker Container
Why Do My Keystrokes Turn into Crazy Characters After I Dump a Bunch of Binary Data into My Terminal
How to Get Jenkins Working with Binaries from a Subfolder of The Root User