The OpenNET Project / Index page

[ новости /+++ | форум | теги | ]




Версия для распечатки Пред. тема | След. тема
Новые ответы [ Отслеживать ]
Является ли скрипт корректным?, !*! Raven, 28-Июл-13, 19:22  [смотреть все]
Есть такой скрипт, практическую задачу выполняет. Задача: проверять доступность сайта,
оповещать голосом, далее работать в цикле до изменения состояния и снова оповещать (только один раз).
Вопрос, является ли этот скрипт корректным с точки зрения программирования на bash?
Вроде бы нет рекурсивных вызовов?
#!/bin/bash
read INPUT
ip="$INPUT"
count=1
sett ()
{
result=$(ping -c ${count} ${ip} | tail -2 | head -1 | awk '{print $4}')
if [ "$result" = "1" ]; then
echo "The website is up" | festival --tts
sett1
else
echo "The website is down" | festival --tts
noset1
fi
}

noset1 ()
{
while true
do
result=$(ping -c ${count} ${ip} | tail -2 | head -1 | awk '{print $4}')
if [ "$result" != "1" ]; then
sleep 5
else
sett
fi
done
}

sett1 ()
{
while true
do
result=$(ping -c ${count} ${ip} | tail -2 | head -1 | awk '{print $4}')
if [ "$result" = "1" ]; then
sleep 5
else
sett
fi
done
}

sett

  • Является ли скрипт корректным?, !*! skb7, 00:44 , 29-Июл-13 (1)
    > Есть такой скрипт, практическую задачу выполняет. Задача: проверять доступность сайта,
    > оповещать голосом, далее работать в цикле до изменения состояния и снова оповещать
    > (только один раз).
    > Вопрос, является ли этот скрипт корректным с точки зрения программирования на bash?

    Считаю что скрипт плохой.
    1. Не знаю как в баше, а в Си есть такое понятие -- стек вызовов, при вызове каждой функции сохраняется адрес возврата и аргументы, переданные в функцию. Следовательно, если вызывать достаточно большую цепочку функций, стек может переполниться, да и память расходуется под этот стек. У вас как раз такая ситуация -- может быть ситуация типа sett() -> sett1() -> sett() -> noset1() -> sett() -> sett1() -> .... (до бесконечности)
    2. Много повторяющихся кусков кода
    3. Вот этот кусок -- тавтология:


    read INPUT
    ip="$INPUT"

    Почему не сделать сразу:

    read ip

    4. Лучше поток вывода ошибок утилиты ping перенаправлять в /dev/null, зачем вам в консоли куча текста "Network is unavailable".
    5.
    if [ "$result" = "1" ]; then

       Если переменная count будет больше единицы, этот код будет некорректен. Нужно сравнивать на равенство переменной count.

    У вас там конечный автомат с двумя состояниями, достаточно блок-схему нарисовать и всё становится просто. Вот как я реализовал бы этот скрипт:


    #!/bin/sh

    ip=0
    count=1
    prev_is_site_up=0
    cur_is_site_up=0

    read_ip() {
        echo -n "Enter site IP: "
        read ip
        # TODO: add some IP address validation here
    }

    # Returns 1 if site is up, 0 -- site is down
    test_site() {
        res=$(ping -c ${count} ${ip} | tail -2 | head -1 | awk '{print $4}') \
            2> /dev/null
        if [ "$res" = $count ]; then
            return 1
        else
            return 0
        fi
    }

    notify() {
        if [ $prev_is_site_up -eq 1 ]; then
            echo "The website is up" | festival --tts
        else
            echo "The website is down" | festival --tts
        fi
    }

    script_init() {
        test_site
        prev_is_site_up=$?
        notify
    }

    script_loop() {
        while :; do
            sleep 5
            test_site
            cur_is_site_up=$?
            if [ $cur_is_site_up -ne $prev_is_site_up ]; then
                prev_is_site_up=$cur_is_site_up
                notify
            fi
        done
    }

    read_ip
    script_init
    script_loop

    Ну и последнее: может лучше сделать это с использованием cron?

    • Является ли скрипт корректным?, !*! Raven, 09:37 , 29-Июл-13 (2)
      skb7, спасибо вам, очень большое, вроде бы в целом понятно.

      Я правильно понимаю script_init выполняется 1 раз и prev_is_site_up=$? принимает значение, полученное в test_site?!

      Про крон думал, конечно, только хотелось понять свои ошибки и найти правильный метод.  

      • Является ли скрипт корректным?, !*! skb7, 01:29 , 30-Июл-13 (3)
        > Я правильно понимаю script_init выполняется 1 раз и prev_is_site_up=$? принимает значение,
        > полученное в test_site?!

        Да, script_init() только раз вызывается. В $? содержится код возврата последней инструкции, т.е. да,  переменная prev_is_site_up принимает значение, которое вернула ф-ция test_site().

        • Является ли скрипт корректным?, !*! Raven, 04:06 , 31-Июл-13 (4)
          >> Я правильно понимаю script_init выполняется 1 раз и prev_is_site_up=$? принимает значение,
          >> полученное в test_site?!
          > Да, script_init() только раз вызывается. В $? содержится код возврата последней инструкции,
          > т.е. да,  переменная prev_is_site_up принимает значение, которое вернула ф-ция test_site().

          Еще раз большое спасибо.

        • Является ли скрипт корректным?, !*! Raven, 10:57 , 12-Авг-13 (5)
          >> Я правильно понимаю script_init выполняется 1 раз и prev_is_site_up=$? принимает значение,
          >> полученное в test_site?!
          > Да, script_init() только раз вызывается. В $? содержится код возврата последней инструкции,
          > т.е. да,  переменная prev_is_site_up принимает значение, которое вернула ф-ция test_site().

          Кстати, как бы эта задача решалась с крон? Он ведь каждый раз запускает скрипт заново
          и обнуляет значения переменных. Или нужно делать лва скрипта?один инит, который запустится один раз запишет куда-то значение пременной..



          • Является ли скрипт корректным?, !*! Raven77, 20:21 , 12-Авг-13 (6)
            Думал-думал. Пока придумал такое, может, не очень изящное решение
            Делается 2 скрипта:
            1. Запускается один раз при старте системы, проверяет сеть и записывает значение в файл с именем сс, например.
            2. Проверят, допустим раз в 5 минут, вот, что получилось:

            #!/bin/bash


            test_site() {
                res=$(ping -c 1 opennet.ru | tail -2 | head -1 | awk '{print $4}') 2> /dev/null
                if [ "$res" = "1" ]; then
                    return 1
                else
                    return 0
                fi
            }
            notify() {
                if [ "$res" -eq "1" ]; then
                    echo "The website is up" | festival --tts
                    echo "1" > cc
                else
                    echo "The website is down" | festival --tts
                    echo "" > cc
                fi
            }
            test_site
            catt=$(cat cc)      
                    if [ "$res" != "$catt" ]; then            
                        notify
                    fi
                      
                    

            • Является ли скрипт корректным?, !*! Raven77, 00:37 , 13-Авг-13 (7)
              Вернее, echo "The website is down" | festival --tts
                      echo "0" > cc
              Так как я проверял на своем роутере, просто отключая сеть, поэтому получалось Network is unreachable, и значение $res было пустым
              И, конечно, забыл сравнивать лучше с переменной count

              • Является ли скрипт корректным?, !*! Raven77, 01:02 , 13-Авг-13 (8)
                Прошу прощения за этим мои упражнения с bash в онлайн режиме. Последний мой вариант получается таким. P.S. Вдруг кому-то пригодится, хотя бы для первоначальных учебных целей.

                          #!/bin/bash

                count=1
                test_site() {
                    res=$(ping -c $count 192.168.1.2 | tail -2 | head -1 | awk '{print $4}') 2> /dev/null
                    if [ "$res" = "1" ]; then
                        return 1
                    else
                        return 0
                    fi
                }
                notify() {
                case $res in
                "") echo "no network" | festival --tts
                    echo "" > cc
                    ;;
                1) echo "The website is up" | festival --tts
                    echo "1" > cc
                    ;;
                0)
                echo "The website is down" | festival --tts
                echo "0" > cc
                ;;
                esac
                }
                test_site
                catt=$(cat cc)      
                        if [ "$res" != "$catt" ]; then            
                            notify
                        fi
                          

                • Является ли скрипт корректным?, !*! skb7, 02:19 , 13-Авг-13 (10)
                  >[оверквотинг удален]
                  >  ;;
                  > esac
                  > }
                  > test_site
                  > catt=$(cat cc)
                  >         if [ "$res" !=
                  > "$catt" ]; then
                  >            
                  > notify
                  >         fi

                  Опять же, вы проверяете на 1 вместо count.
                  Вот мой вариант скрипта (из моего предыдущего по времени сообщения) с учетом "no network":


                  #!/bin/sh

                  # "ip" can be passed from crontab or be read from some config file
                  ip=8.8.8.8
                  count=1
                  prev_is_site_up=0
                  cur_is_site_up=0
                  var_file=/tmp/prev-is-site-up

                  write_var() {
                          echo -n "$1" > $var_file
                  }

                  read_var() {
                          cat $var_file
                  }

                  # Returns: 0 - site is down, 1 - site is up, 2 - no network
                  test_site() {
                      res=$(ping -c ${count} ${ip} | tail -2 | head -1 | awk '{print $4}') \
                          2> /dev/null

                      case "$res" in
                      "")
                          return 2
                          ;;
                      "$count")
                          return 1
                          ;;
                      *)
                          return 0
                      esac
                  }

                  notify() {
                      case "$1" in
                      0)
                          msg="The website is down"
                          ;;
                      1)
                          msg="The website is up"
                          ;;
                      2)
                          msg="No network"
                          ;;
                      esac

                      echo "$msg" | festival --tts
                  }

                  do_site_check() {
                      test_site
                      cur_is_site_up=$?

                      if [ ! -e $var_file ]; then
                          write_var $cur_is_site_up
                          notify $cur_is_site_up
                          exit 0
                      fi

                      prev_is_site_up=$(read_var)
                      if [ $cur_is_site_up -ne $prev_is_site_up ]; then
                          write_var $cur_is_site_up
                          notify $cur_is_site_up
                      fi
                  }

                  do_site_check


            • Является ли скрипт корректным?, !*! skb7, 02:05 , 13-Авг-13 (9)
              > Пока придумал такое, может, не очень изящное решение
              > Делается 2 скрипта:
              > 1. Запускается один раз при старте системы, проверяет сеть и записывает значение в файл > с именем сс, например.
              > 2. Проверят, допустим раз в 5 минут

              Ну, скрипт будет тот же самый (а не 2).
              Я бы сделал так:


              #!/bin/sh

              # "ip" can be passed from crontab or be read from some config file
              ip=8.8.8.8
              count=1
              prev_is_site_up=0
              cur_is_site_up=0
              var_file=/tmp/prev-is-site-up

              write_var() {
                      echo -n "$1" > $var_file
              }

              read_var() {
                      cat $var_file
              }

              # Returns 1 if site is up, 0 -- site is down
              test_site() {
                  res=$(ping -c ${count} ${ip} | tail -2 | head -1 | awk '{print $4}') \
                      2> /dev/null
                  if [ "$res" = $count ]; then
                      return 1
                  else
                      return 0
                  fi
              }

              notify() {
                  if [ $cur_is_site_up -eq 1 ]; then
                      echo "The website is up" | festival --tts
                  else
                      echo "The website is down" | festival --tts
                  fi
              }

              do_site_check() {
                  test_site
                  cur_is_site_up=$?

                  if [ ! -e $var_file ]; then
                      write_var $cur_is_site_up
                      notify
                      exit 0
                  fi

                  prev_is_site_up=$(read_var)
                  if [ $cur_is_site_up -ne $prev_is_site_up ]; then
                      write_var $cur_is_site_up
                      notify
                  fi
              }

              do_site_check

              В /etc/crontab нужно вызывать этот скрипт с использованием блокировки (с помощью flock); например:


              * * * * * root /usr/bin/flock -n /tmp/check-site-lock -c '/tmp/check-site.sh'

              При старте системы особого смысла вызывать этот скрипт в принципе не вижу, особенно если он каждую минуту будет запускаться. Но если и вызывать, то использовать такую же блокировку, как и в /etc/crontab.

              • Является ли скрипт корректным?, !*! Raven77, 17:03 , 13-Авг-13 (11)
                Круто, глядя, на ваши скрипты понимаю, как много еще не знаю, спасибо, с такими интересными примерами скриптов изучение bash становится действительно интересным.

                Странно, что мне в голову не пришло, проверять существования файла...

              • Является ли скрипт корректным?, !*! Raven77, 16:43 , 18-Авг-13 (12)
                Думал еще на днях над похожим скриптом - голосовым информером магнитных бурь, собственно, тоже самое, но пришла такая мысль, что может, проще добавить логическое "и" для проверки.
                Или это хуже?
                И конкретный вопрос по башу. Можно ли сравнивать  сначала сроку, потом числа?
                "$A" != "возмущено" -a "$cattt" -eq "1" ];
                Полный скрипт ниже:

                #!/bin/bash
                var_file=~/mess_magg

                test_mag () {
                    cattt=$(cat $var_file)
                    A=$(/usr/bin/curl http://www.tesis.lebedev.ru/magnetic_storms.html | /usr/bin/iconv -f cp1251 -t utf-8 | /bin/grep -o возмущено) 2>/dev/null
                if [ "$A" = "возмущено" -a "$cattt" -eq "0" ]; then
                    echo "1" > $var_file
                     notif
                fi

                if [ "$A" != "возмущено" -a "$cattt" -eq "1" ]; then
                    echo "0" > $var_file
                     notif
                fi
                }

                notif () {
                if [ "$A" = "возмущено" ]; then
                    echo "Attention! Magnetic Storm" | festival --tts
                      
                fi
                  if [ "$A" != "возмущено" ]; then
                   echo "There is no anymore" | festival --tts
                  
                fi
                }

                if [ ! -e $var_file ]; then
                   touch $var_file
                    echo "0" > $var_file
                fi

                test_mag

                Прошу прощения за безграмотность, что означает flock? Как-то не обратил на это должного внимания, поисковик ничего не принес внятного.

                • Является ли скрипт корректным?, !*! allez, 19:31 , 18-Авг-13 (13)
                  > И конкретный вопрос по башу. Можно ли сравнивать  сначала сроку, потом
                  > числа?
                  >  "$A" != "возмущено" -a "$cattt" -eq "1" ];

                  Да, можно.

                • Является ли скрипт корректным?, !*! skb7, 16:35 , 19-Авг-13 (14)
                  > Можно ли сравнивать  сначала сроку, потом числа?
                  > [ "$A" != "возмущено" -a "$cattt" -eq "1" ];

                  Да, можно.


                  $ help test | grep AND
                        EXPR1 -a EXPR2 True if both expr1 AND expr2 are true.

                  Не важно, строки вы сравниваете или числа, -- и то, и то -- выражения, так что всё ок.

                  Можно и так, как вы сделали (в контексте test), а можно средствами баша:

                  1. [ $x != "abc" -a $y -eq 2 ]
                  2. [ $x != "abc" ] && [ $y -eq 2 ]

                  Эти выражения делают одно и то же.

                  > может, проще добавить логическое "и" для проверки.
                  > Или это хуже?

                  Ну, если так сильно углубляться...

                  1. Я бы переделал этот кусок кода:


                  if [ "$A" = "возмущено" -a "$cattt" -eq "0" ]; then
                      echo "1" > $var_file
                       notif
                  fi

                  if [ "$A" != "возмущено" -a "$cattt" -eq "1" ]; then
                      echo "0" > $var_file
                       notif
                  fi

                  таким образом:


                  if [ "$A" = "возмущено" ]; then
                      if [ $cattt -eq 0 ]; then
                          echo 1 > $var_file
                          notif
                      fi
                  else
                      if [ $cattt -eq 1 ]; then
                          echo 0 > $var_file
                          notif
                      fi
                  fi

                  Лучше тем, что будет делаться меньше проверок. У вас будет выполняться 4 проверки, а у меня 2. Плюс я избавился от ненужных кавычек.

                  2. Еще вот этот кусок кода переделал бы:


                  if [ "$A" = "возмущено" ]; then
                      echo "Attention! Magnetic Storm" | festival --tts
                        
                  fi
                    if [ "$A" != "возмущено" ]; then
                     echo "There is no anymore" | festival --tts
                    
                  fi

                  на такой:


                  if [ "$A" = "возмущено" ]; then
                      echo "Attention! Magnetic Storm" | festival --tts
                  else
                      echo "There is no anymore" | festival --tts
                  fi

                  ...или даже на такой:


                  if [ "$A" = "возмущено" ]; then
                      msg="Attention! Magnetic Storm"
                  else
                      msg="There is no anymore"
                  fi

                  echo $msg | festival --tts

                  > что означает flock?

                  flock -- блокировка с помощью файла. Такой себе аналог мьютекса. Используется, если вы подозреваете, что скрипт может быть выполнен одновременно из нескольких параллельных потоков (процессов), чтобы избежать race conditions.

                  Суть в том, что каждый раз при выполнении скрипта команда flock проверяет, существует ли lock-файл;
                  1. если не существует -- создается такой файл и выполняется код, защищенный этим локом
                  2. если существует -- команда flock будет ждать, пока lock-файл исчезнет (т.е. этот скрипт, выполняющийся в другом потоке, закончит выполнять защищенный локом код и удалит lock-файл), и потом продолжит выполнять код скрипта (защищенный flock). С помощью параметров также можно задать поведение: например, ждать с таймаутом, а потом завершать скрипт, либо просто сразу завершать скрипт.

                  Чтобы понять, как работает flock, можете проделать следующий эксперимент:
                  1. В одном терминале запустите команду


                  flock /tmp/123 -c 'while :; do sleep 1; done'

                  2. В другом терминале запустите команду

                  flock /tmp/123 -c 'echo 1'

                  - flock во втором терминале будет "висеть" и ждать, пока файл /tmp/123 удалится
                  - файл /tmp/123 удалится тогда, когда завершиться flock в первом терминале
                  - а в первом терминале под локом выполняется бесконечный цикл
                  3. Теперь нажмите Ctrl+C в первом терминале, чтобы завершить первый flock
                  4. Во втором терминале сразу же выполнится команда "echo 1".
                  • Является ли скрипт корректным?, !*! Raven77, 20:26 , 19-Авг-13 (15)
                    skb7, спасибо, что так возитесь со мной.
                    С flock теперь понял.
                    • Является ли скрипт корректным?, !*! skb7, 22:32 , 19-Авг-13 (16)
                      > skb7, спасибо, что так возитесь со мной.

                      На такие вопросы интересно отвечать, -- сам учишься в процессе. Ведь вы дали уже готовое решение и попросили улучшить. That's the spirit :)

                      • Является ли скрипт корректным?, !*! Raven77, 22:38 , 19-Авг-13 (17)
                        Спасибо Тимуру Гатину (хоть и не знаком с ним в жизни), идея скрипта принадлежит ему, я случайно наткнулся на его топик в одном сообществе по программированию в VK и заинтересовался, строго из спортивного интереса.
                        Подумал, смогу ли со своими скудными знаниями, почти на чистой логике сделать хоть что-то, пусть и не очень правильно, но прошло время и решил все-таки понять, как это делается правильно...
                        Вообще изучение bash оказалось очень увлекательным, в каком-то смысле благодаря этому увлечению нашел работу, хоть и не связанную с программированием :) и вообще IT




Партнёры:
PostgresPro
Inferno Solutions
Hosting by Hoster.ru
Хостинг:

Закладки на сайте
Проследить за страницей
Created 1996-2024 by Maxim Chirkov
Добавить, Поддержать, Вебмастеру