Linux How to take hostfile as an input from user and call it into script

Viewed 70

I am using the below script for pinging the multiple Linux hosts which works perfectly fine if it put the hostfile by hardcoding into script itself but i want that to be on user input based.

While i am using read the file as an input from user but thats getting fail, please suggest me what i am doing wrong here.

#!/bin/bash
read -rsp $'Please Enter your hostFile name: ' target_File
echo $target_File
printf "$(date) Starting the Ping Check...."
function pingHost () {
    target_host=${1}
    ping -c2 ${target_host} >/dev/null 2>&1 &&
    printf "%-20s %-10s\n" "host ${target_host}" "ping SUCCESS" ||
    printf "%-20s %-10s\n" "host ${target_host}" "ping FAILED"
}
#
# Variable "data" seeks the filename containing host list.
data="$(< target_File)"
for line in $data
do
    pingHost ${line} &
done
wait
printf "Completed @=> $(date)"
printf "\n"

While giving the hostfile inside the script then it works as follows ..

data="$(< hostfile)" <-- this hard-coded file containing hosts

$ ./ping_parallel.bash
Sun May 22 10:55:24 IST 2022 Starting the Ping Check....

host savfav0194         ping SUCCESS
host savfav0268         ping SUCCESS
host savfav0263         ping SUCCESS
host savfav0196         ping SUCCESS
host savfav0260         ping SUCCESS
host savfav0259         ping SUCCESS
host savfav2088         ping SUCCESS
host savfav2135         ping SUCCESS
host savfav2136         ping SUCCESS
host savfav3088         ping SUCCESS
host savfav0257         ping SUCCESS
host savfav0262         ping SUCCESS
host savfav0261         ping SUCCESS
host savfav0270         ping SUCCESS
host savfav0255         ping SUCCESS
host savfav0265         ping SUCCESS
host savfav0266          ping FAILED

Completed @=> Sun May 22 10:55:26 IST 2022

Failing while trying user input :

Please Enter your hostFile name: target_File
Sun May 22 10:58:54 IST 2022 Starting the Ping Check...../ping_parallel.bash: line 22: target_File: No such file or directory
Completed @=> Sun May 22 10:58:54 IST 2022
3 Answers

As mentioned in the comment and also a good practice to use while over for, please refer for bash manual here.

I just modified it with while, hopefully it should the Job for you!

#!/bin/bash
read -p $'Please Enter your hostFile name: ' target_File
printf "$(date) Starting the Ping Check...."
function pingHost () {
    target_host=${1}
    ping -c2 ${target_host} >/dev/null 2>&1 &&
    printf "%-20s %-10s\n" "host ${target_host}" "ping SUCCESS" ||
    printf "%-20s %-10s\n" "host ${target_host}" "ping FAILED"
}
while read -r line;
do
    pingHost $line &
done < $target_File
wait
printf "Completed @=> $(date)"
printf "\n"

Slight modification with your Script:

#!/bin/bash
read -rp $'Please Enter your hostFile name: ' target_File
# Don't use variables in the printf format string. Use printf '..%s..' "$(date)".
printf "Starting the Ping Check %s ....\n" "$(date)"
# You Can Just use function name hence its your choice, i just removed.
# pingHost i changed to "icmp_echo" as i used that 
# function pingHost  () {
icmp_echo  () {
    target_host=${1}
    ping -c2 "${target_host}"  >/dev/null 2>&1 &&
    printf "%-32s %-10s\n" "host ${target_host}" "ping SUCCESS" ||
    printf "%-32s %-10s\n" "host ${target_host}" "ping FAILED"
}
# Use a while loop and the read command. Where The -r option to read prevents backslash interpretation.By default,read modifies each line read, 
# by removing all leading and trailing whitespace characters (spaces and tabs, if present in IFS).If that is not desired, the IFS variable may 
# be cleared
while IFS= read -r line
do
    icmp_echo  "$line" &
# Double quote to prevent globbing and word splitting.
done < "$target_File"
wait
# Don't use variables in the printf format string. Use printf '..%s..' "$(date)".
printf "Completed @=> %s" "$(date)"
printf "\n"

Continuing from the comment and request for an answer, you have a number of small issues with your script you should address. First, always paste your script into ShellCheck and fix all problems before posting here (it really is quite good).

The other issues found (some material, some form) are:

  • POSIX function definitions are simply name() { body }. The word function which is a bashism allows for defining functions as function name { body }, and yes bash will even accept a combination. For portable scripts always use name() { body } only.
  • Do not embed expandable text or variables in the printf format-string. That's what conversion-specifiers are for. Don't combine fixed-text with variables in the strings you send through format-specifiers in the format-string. Fixed text goes in the format-string itself, e.g.

NOT

    echo $target_File
    printf "$(date) Starting the Ping Check...."
    #...
    printf "%-20s %-10s\n" "host ${target_host}" "ping SUCCESS"

Instead

    printf "%s\n%s Starting ping check...\n" "hostsfile" "$(date)"
    #...
    printf "host %-20s ping SUCCESS\n" "$1"
  • Validate, validate, validate - especially user-input and filenames
  • Don't use list=$(<file) and for i in "$list"; do ... done, Instead, it is while read -r line; do ... done < file

Putting all the pieces together, you can do something similar to the following:

#!/bin/bash --norc

## function
pinghost() {
    ## validate argument provided
    [ -z "$1" ] && {
        printf "error: no host provided.\n" >&2
        return;
    }
    
    if ping -c2 "$1" >/dev/null 2>&1 ; then       ## check ping success / failure
        printf "host %-20s ping SUCCESS\n" "$1"   ## use fixed-text / specifiers properly
    else
        printf "host %-20s ping FAILED\n" "$1"
    fi
}

## destination hosts, validate filename given and file exists and has non-zero size
if [ -n "$1" ] && [ -s "$1" ]; then
    hostsfile="$1"
else
    printf "error: hostsfile argument required to non-zero size file.\n" >&2
    exit 1
fi

## never embed expandable output in the printf format-string, use proper spedifiers
# printf "$(date) Starting the Ping Check...."

printf "%s\n%s Starting ping check...\n" "$hostsfile" "$(date)"

## loop over each line in hosts file
while read -r host; do
    pinghost "$host"      ## no sense in backgrounding just to wait
done < "$hostsfile"

printf "Completed @=> %s\n" "$(date)"   ## the '\n' belongs in the format-string

Let me know if you have further questions.

You said that dosen't work (changing data=$(< target_File) to data=$(< $target_File))

$ target_File=hosts.txt
$ data=$(< target_File)
bash: target_File: No such file or directory
$ echo $data

$ data=$(< $target_File)
$ echo $data
google.com amazon.com

# seems to be fine
$ for x in $data; do echo pingit $x; done
pingit google.com
pingit amazon.com

read

$ read target_File
hosts.txt
$ data=$(< $target_File)
$ echo $data
google.com amazon.com
Related