8

I have a set of servers mentioned in a text file called network_list.txt . How can i run through the servers and run command and display the result ? I have tried the follows :

filename="network_list.txt"
service=httpd
while read -r line
do
    name="$line"
    ping -c 3 $name > /dev/null 2>&1;
    RETVAL=$?
        if [ $RETVAL -ne 0 ]
        then 
           echo "$name is down"
        else 
            echo "$name is up and running"
        fi
    http_status=$(ssh $name ps -ef | grep -v grep | grep $service | wc -l)
        if [ $http_status -ne 0 ]
        then
            echo "$service is up and running in $name"
        else
            echo "$service is down in $name"
        fi
done < "$filename"

But it stops after first server .

Thanks :) .

6
  • What is the format of your input file? Dec 5, 2017 at 10:54
  • its like 192.168.1.101 192.168.1.102 192.168.1.103 - - - - - - line by line
    – KTM
    Dec 5, 2017 at 10:55
  • 6
    This seems to me like the perfect use case for Ansible or some other Configuration Management tool.
    – GregL
    Dec 5, 2017 at 11:37
  • 1
    I second Greg's advice to use Ansible. I used to have scripts like this with a for loop. Now I put those host names into an Ansible configuration file and tell Ansible to run a script on each of them. Ansible gives you the ability to write and test the script locally, then to run it remotely on each server. Half the stuff that my scripts used to do is handled by Ansible modules, so I have a lot less to maintain. A+, will never go back. Dec 5, 2017 at 19:49
  • 2
    salt would work well for this too. It would basically be: salt \* cmd.run "systemctl is-active httpd" Dec 5, 2017 at 23:22

4 Answers 4

7

You need to redirect the stdin of SSH command in your loop to nowhere to prevent reading all your lines. This should do it:

http_status=$(ssh $name "ps -ef | grep -v grep | grep $service | wc -l" < /dev/null)
1
  • ping_stat=$(ping -c 4 -q $name 2>&1) , Hi why couldn't i use $name after this command ?
    – KTM
    Dec 5, 2017 at 11:44
8

There is solution with -n -x parameters:

http_status=$(ssh -nx name ps -ef | grep -v grep | grep $service | wc -l)

The -x disables X11 forwarding to get rid of possible X11 forwarding request failed messages.

The -n redirects stdin from /dev/null (actually, prevents reading from stdin).

1

While the answer that is accepted totally works, I'd like to just offer this script that might be useful should someone come across it.

Usage

So you would obviously need to change the lines under the "Echo $server $serverIP" lines to reflect the actual commands you'd like to use. $1 is the first thing you type after "./scriptname.sh" - e.g. ./scriptname.sh $1 $2 $3... - and here is how this breaks down:

$1 should be the file with your hostnames. $2 is the positional parameter you pass down to FunctionC (which always gets called in this example script). $2 and $3 can be used to call the other Functions (A/B) - alternatively you could replace $3 and $4 at the EOF with "FunctionA" and "FunctionB" if you always want to run them.

Hope this helps someone, it's been INVALUABLE to me whilst deploying packages or testing things across the domain.

#! /bin/bash
SVRList=`ls ./$1`

#========[ FUNCTIONS ]===============

FunctionA () {
  for server in `cat $SVRList | grep -v ^#`  # Makes variable "server" from each line in $SVRlist that is not commented
do
  serverIP=$(nslookup $server | tail -2 | awk -F ":" '{print $2}')
  echo "============= $server | $serverIP ============="
  #command that you'd like to run locally
done
}

FunctionB () {
      for server in `cat $SVRList | grep -v ^#`
    do
      passedvar="192.16.${1}"
      serverIP=$(nslookup $server | tail -2 | awk -F ":" '{print $2}')
      echo "============= $server | $serverIP ============="
      ssh -q $serverIP '#command that you'd like to run remotely'
      ssh -q $serverIP '#second command, if you so fancy'
      ssh -q $serverIP "ping $passedvar"
      #^ is done just to show an example of what can be done
    done
    }

FunctionC () {
  for server in `cat $SVRList | grep -v ^#`
do
  passedvar="192.16.${1}"
  serverIP=$(nslookup $server | tail -2 | awk -F ":" '{print $2}')
  echo "============= $server | $serverIP ============="
  ssh -q $serverIP "ping $passedvar"
  #^ is done just to show the power of positional parameters.
done
}

#========[ SCRIPT ]==========

if [ $# -lt 2 ] 
  then
  printf "Usage: %s <file containing list of server hostnames> <passed variable> <functions> \n" "$(basename "$0")" >&2
  exit 64
  else 
  echo "Starting Script"
fi

FunctionC $2
$3
$4
exit

Also OP:

Why set service=httpd? It may be nicer to use [ testing and "service=$1" where $1 could be httpd, splunkd, or any other service. Additionally, the variable http_status should be changed to something like "service_status" to make the script better documented.

1

Why not use pssh?

https://github.com/lilydjwg/pssh

It's a parallel SSH tool which has built in support for obtaining a list of hosts from a file:

pssh -h network_list.txt ps -ef | grep -v grep | grep $service | wc -l

Then you can parse the output as you like

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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