Linux 如何将主机文件作为用户的输入并将其调用到脚本中

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

我正在使用下面的脚本来 ping 多个 Linux 主机,如果它通过硬编码将 hostfile 放入脚本本身,它会工作得很好,但我希望它基于用户输入。

虽然我正在使用 read 文件作为用户的输入,但那是失败的,请告诉我我在这里做错了什么。

#!/bin/bash
read -rsp $'Please Enter your hostFile name: ' target_File
echo $target_File
printf "$(date) Starting the Ping Check...."
function pingHost () {
    target_host=
    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"

在脚本中给出 hostfile 然后它的工作方式如下..

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

尝试用户输入时失败:

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

你说那行不通(将 data=$(< target_File) 更改为 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 target_File
hosts.txt
$ data=$(< $target_File)
$ echo $data
google.com amazon.com

如评论中所述,也是使用 while 而不是 for 的良好做法,请在此处参考 bash manual

我刚刚用 while 修改了它,希望它适合你!

#!/bin/bash
read -p $'Please Enter your hostFile name: ' target_File
printf "$(date) Starting the Ping Check...."
function pingHost () {
    target_host=
    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"

对脚本稍作修改:

#!/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=
    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"

继续评论和请求答复,您的脚本有许多小问题您应该解决。首先,请始终将您的脚本粘贴到 ShellCheck 并解决所有问题,然后再发布到这里(真的很好)。

发现的其他问题(一些 material,一些形式)是:

  • POSIX 函数定义就是 name() { body }function 一词是 bash 主义,允许将函数定义为 function name { body },是的 bash 甚至可以接受组合。对于可移植脚本,始终仅使用 name() { body }
  • 不要在 printfformat-string 中嵌入可扩展文本或变量。这就是 conversion-specifiers 的用途。不要将 fixed-text 与您通过 format-specifiers 发送的字符串中的变量组合在 format-string 中。固定文本进入 format-string 本身,例如

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

改为

    printf "%s\n%s Starting ping check...\n" "hostsfile" "$(date)"
    #...
    printf "host %-20s ping SUCCESS\n" ""
  • 验证,验证,验证 - 特别是 user-input 和文件名
  • 不要用list=$(<file)for i in "$list"; do ... done,而是while read -r line; do ... done < file

把所有的部分放在一起,你可以做类似下面的事情:

#!/bin/bash --norc

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

## destination hosts, validate filename given and file exists and has non-zero size
if [ -n "" ] && [ -s "" ]; then
    hostsfile=""
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

如果您还有其他问题,请告诉我。