Как и Ромео, я рекомендую использовать 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. Он пингует их как фоновый процесс, чтобы попытаться немного ускорить работу. Затем он перебирает идентификаторы PID фоновых команд ping и ожидает их завершения. Если проверка связи прошла успешно, он проверяет журнал, чтобы увидеть, была ли последняя запись для этого хоста ошибкой, если это так, он регистрирует запись up для этого хоста, если нет, он ничего не делает. Если команда ping не удалась, она проверит журнал, чтобы убедиться, что последняя запись для этого хоста была успешной, если это так, она зарегистрирует запись down для этого хоста, если нет, она ничего не сделает.
Я бы использовал ассоциативный массив для хранения статуса и регистрации только изменений статуса. Примерно так:
#!/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