呼叫所有 bash 专家,请给我一个正确的方向。以下内容仅对 1 台主机执行了我需要的操作,但不可靠,并且记录了太多行。如何让它更优雅、更实用?理想情况下,我希望它能够 ping 多个数据包并仅记录更改。另外,ping 几台主机。当连续多个数据包无法访问/无法访问主机时,仅记录一行。
#!/bin/bash
while [ 1 ]; do
ping -c 1 -w 2 $1
if [[ $? == 0 ]]; then
spd-say "up"
notify-send "up"
echo "up $1 $(date +%Y%m%d-%H%M)" >> /tmp/ping.log
else
echo "down $1 $(date +%Y%m%d-%H%M)" >> /tmp/ping.log
fi
sleep 2m
done;
答案1
像罗密欧一样,我建议使用 cron 而不是无限循环,但为了解决您的日志记录问题,我想出了:
#!/usr/bin/env bash
hosts=("$@")
log=~/tmp/ping.log
[[ ! -f "$log" ]] && touch "$log"
check_log () {
local h=$1
local s
s=$(awk -v h="$h" '$2 == h {print $1}' "$log" | tail -1)
printf '%s' "$s"
}
for host in "${hosts[@]}"; do
ping -qc 5 "$host" >/dev/null 2>&1 &
pids+=([$!]="$host")
done
for pid in "${!pids[@]}"; do
host=${pids[$pid]}
s=$(check_log "$host")
if wait "$pid"; then
if [[ "$s" == down || -z "$s" ]]; then
printf '%s\n' "up $host $(date +%Y%m%d-%H%M)" >> "$log"
fi
else
if [[ "$s" == up || -z "$s" ]]; then
printf '%s\n' "down $host $(date +%Y%m%d-%H%M)" >> "$log"
fi
fi
done
该check_log
函数将在日志文件中搜索给定主机的条目,特别是该主机的最后一个条目以及它是启动还是关闭。
该脚本将循环遍历每个主机,并使用 5 个数据包对它们进行 ping 操作。它将作为后台进程对它们进行 ping 操作,以尝试加快速度。然后它循环遍历后台 ping 命令的 PID 并等待它们完成。如果 ping 成功,它会检查日志以查看该主机的最后一个条目是否失败,如果是,则会记录一条向上该主机的条目,如果不是,则不执行任何操作。如果 ping 命令失败,它将检查日志以查看该主机的最后一个条目是否成功,如果成功,它将记录向下该主机的条目,如果不是,则不执行任何操作。
答案2
我将使用关联数组来保存状态并仅记录状态更改。像这样的东西:
#!/usr/bin/env bash
## This will let us use the host names as keys in the associative array 'status'
declare -A status
while :; do
for host in "$@"; do
## Ping the server and, if the ping is successful, set $isUp to "Up",
## if the ping fails, set $isUp to "Down".
ping -c 1 -w 2 "$host" &>/dev/null &&
isUp="Up" || isUp="Down"
## If the current value of $isUp isn't the same as what is stored in the
## status array for this server, we should report it.
if [[ ${status[$host]} != $isUp ]]; then
spd-say "$host is $isUp"
notify-send "$host is $isUp"
printf "%s is %s\n" "$host" "$isUp" >> /tmp/ping.log
## save the current status in the array for this server.
status[$host]=$isUp
fi
done
sleep 2s;
done
然后您可以使用主机名作为参数来运行它:
checkHost.sh hostname1.foo.com hostanme2.bar.com
如果您无法使用关联数组(如果您运行的是旧的 bash 版本),您可以使用两个单独的数组:
hosts=("$@");
while :; do
for ((i=0;i<${#hosts[@]}; i++)); do
host=${hosts[i]};
ping -c 1 -w 2 "$host" &>/dev/null &&
isUp="Up" || isUp="Down"
if [[ ${status[i]} != $isUp ]]; then
spd-say "$host is $isUp"
notify-send "$host is $isUp"
printf "%s is %s\n" "$host" "$isUp" >> /tmp/ping.log
status[i]=$isUp
fi
done
sleep 2s;
done