Использование Ping параллельно в скрипте Bash - PullRequest
1 голос
/ 05 мая 2019

Поэтому я пытаюсь пропинговать диапазон адресов параллельно в скрипте bash, подсчитывать живые и распечатывать их. Скрипт работает с пингом адресов и распечатывает живые адреса, но всегда выдает:

"Было 0 онлайн-хостов и 254 оффлайн-хостов"

Он не увеличивает переменную ALIVE в моем коде, возможно, потому что он находится в подоболочке? Как я мог обойти это? Вот что у меня сейчас:

#!/bin/bash

TOTAL=0
ALIVE=0

if [ $# -eq 3 ]
then
    echo -n 'Live hosts:'
    for ((i = $2; i <= $3 && i <= 254; ++i))
    do
        ((++TOTAL))
        ping -c 1 -i 0.2 -w 1 -W 1 $1.$i > /dev/null && ((++ALIVE)) && echo "    $1.$i" &
    done

    echo "There were $ALIVE online hosts and $((($TOTAL - $ALIVE))) offline hosts"
else
    echo "USAGE: pingRange.sh <first 3 octets of ip> <last octet start> <last octet end>"
    echo "    Ex: pingRange.sh 192.168.0 1 254

Примечание: пример ввода для скрипта показан в части "else".

Примечание 2: Да, я знаю, что nmap проще, я уже написал рабочий скрипт с nmap, сейчас пытаюсь сделать его для ping.

Примечание 3: Я использовал временный файл, и он работал, обновленный код имеет комментарий #NEW:

#!/bin/bash

if [ $# -eq 3 ]
then
    TOTAL=0 #NEW
    TEMP=mktemp #NEW
    echo -n 'Live hosts:'
    for ((i = $2; i <= $3 && i <= 254; ++i))
    do
        ((++TOTAL))
        ping -c 1 -i 0.2 -w 1 -W 1 $1.$i > /dev/null && echo "    $1.$i" >> $TEMP & #NEW
    done
    wait #NEW
    cat $TEMP

    ALIVE=$(cat $TEMP | wc -l) #NEW
    echo "There were $ALIVE online hosts and $((($TOTAL - $ALIVE))) offline hosts"

    rm $TEMP #NEW
else
    echo "USAGE: pingRange.sh <first 3 octets of ip> <last octet start> <last octet end>"
    echo "    Ex: pingRange.sh 192.168.0 1 254

Ответы [ 2 ]

1 голос
/ 05 мая 2019

Я полагаю, что вы можете сделать это, используя wait.

Изменение вашего кода, что-то вроде (не проверено):

#!/bin/bash

TOTAL=0
ALIVE=0

if [ $# -eq 3 ]
then
    unset pids
    declare -A pids
    echo -n 'Live hosts:'
    for ((i = $2; i <= $3 && i <= 254; ++i))
    do
        ((++TOTAL))
        ping -c 1 -i 0.2 -w 1 -W 1 $1.$i > /dev/null &
        pids[$i]=$!
    done

    for i in "${!pids[@]}"
    do
        wait ${pids[$i]} && ((++ALIVE)) && echo "    $1.$i"
    done

    echo "There were $ALIVE online hosts and $((($TOTAL - $ALIVE))) offline hosts"
else
    # ...
  • unset / declare - на всякий случай
  • ping ... & все еще запускает команду в фоновом режиме
  • pids[$i]=$! сохраняет свой pid
  • for ... зацикливается на клавишах
  • wait ${pids[$i]} возвращает состояние выхода после выполнения команды cmd
  • && ... делает то же, что и раньше
1 голос
/ 05 мая 2019
... && ((++ALIVE)) && ... &

Это выражение выполняется внутри другого процесса, поэтому изменения не отображаются в родительском процессе

a=1
((++a)) &    # <- is run as another process
echo "$a"  # this will print 1 ...

(
    ((++a))
    echo "$a"
) &          # this will print 2 in the background

wait

Итак, мы хотим запустить (( $3 - $2 )) процессов. Каждый из этих процессов будет выполняться одновременно. Нам нужно получить результат от всех этих процессов в конце. Мы приходим к тому, что мы называем «синхронизацией». Нам нужно синхронизировать все значения всех процессов в одной точке.
Мы можем представить, используя экс. 255 файлов, один файл уникален для каждого процесса. Затем после выполнения childs мы можем запросить файлы.
Проще всего использовать стандартный вывод или другой поток с буферизацией строки:

live_hosts=$(
   for ((i = $2; i <= $3 && i <= 254; ++i)); do
       # `if` is more readable then `a && b`
       (
          if ping -c 1 -i 0.2 -w 1 -W 1 "$1.$i" >/dev/null; then
              echo "$1.$i"
          fi
       ) &
   done
   wait  # remember to wait for all the childs
)

Поскольку stdout должен быть буферизованной строкой, кратное число echo "$1.$i" не должно перехватывать запись, поэтому мы должны получить только переменную со строками. Тогда вы можете просто:

echo "There were $(printf "$live_hosts" | wc -l) online hosts"

Но мы могли бы сделать это с помощью временного каталога:

tmpdir=$(mktemp -d)

for ((i = $2; i <= $3 && i <= 254; ++i)); do
   (
      if ping -c 1 -i 0.2 -w 1 -W 1 "$1.$i" >/dev/null; then
           # create a file with the name "$i" inside tmpdir
           # I don't think content matters (just the name of file)
           touch "$tmpdir"/"$i"
       fi 
    ) &
done
wait

# ex. the count of alives are the count of files inside out tmpdir
alive=$(find "$tmpdir" -type f -print . | wc -c)

# this is funny
for i in "$tmpdir"/*; do
      echo "$1.$i is alive!"
done

# remember to cleanup
rm -r "$tmpdir"

И просто для того, чтобы сделать это интересным, и, поскольку мы любим единомышленников, вот решение, использующее xargs и seq:

live_hosts=$(seq -f "$1.%.0f" "$2" "$3" | xargs -n1 -P0 -- sh -c 'ping -c 1 -i 0.2 -w 1 -W 1 "$1" >/dev/null && echo "$1"' --)
alive=$(echo "$live_hosts" | wc -l)
# well, if just the count matters, add the `| wc -l` to the one liner ..
...