- Является ли скрипт корректным?, 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/ship=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_maggtest_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" fiecho $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
|