BASH Tips&Tricks #000C: Lets try it now! 9.04.2013 http://sysadminblog.ru/bash/2013/04/09/bash-tipstricks-000c-lets-try-it-now.html Давайте попробуем это сейчас! Да-да, именно вот этот чудесный кусочек кода выполним, а он нам взрыхлит почву, посеет нужные семена. А потом следующий за ним код, ещё более великолепный в своём совершенстве, даст сочные зелёные побеги. И уже в завершение всего этого благолепия наш скрипт пожнёт плоды, свяжет снопы и сложит стога… Жаль, что вся этя идиллия оказывается досадной фикцией, когда наш трудолюбивый созидательный код вдруг помещают в условия неблагоприятного марсианского климата: в результате мало того, что ничегошеньки не всходит, так ещё и скрипт с оглушительным треском рушится. А может статься и того хуже: как ни в чём ни бывало начнёт выполняться следующий код, мерно перепахивающий красные марсианские пески в задумчивом цикле без конца и края… Всё ещё не слишком ясно, о чём таком внеземном у нас сегодня пойдёт речь? ОК, я немного увлёкся научной фантастикой, так что постараюсь теперь пояснить ближе к реалиям повседневного скриптотворчества. Вот смотрите: вы, я, он, она и они — то есть «все мы», — часто пишем скрипты с мимимальными проверками на корректность завершения команд. И это, в принципе, не особо-то и плохо: ведь в BASH, к сожалению, нет такой волшебной палочки-выручалочки, как возможность устанавливать собственные обработчики исключительных ситуаций. Проверять же код возврата каждого grep'а или sed'а было бы откровенно глупо. С другой стороны, аварийное завершение скрипта или его продолжение после того, как произошла непредвиденная ошибка — часто бывает в равной мере нежелательно, а иногда просто катастрофично. Посему выполнение наиболее критичных участков кода обязательно необходимо контролировать на предмет возможного возникновения ошибок, при этом исключая самопроизвольное «падение» скрипта. Как это сделать? Для подобных вещей в BASH существует замечательная, хоть и весьма неспешная, функция eval: она безопасным образом выполняет переданный ей в качестве аргумента код, компилируя его прямо во время исполнения скрипта. При этом eval может выполнить как одну команду, так и целый блок кода произвольного размера. Недостатком eval является то, что в случае возникновения ошибки легко можно отследить сам факт её возникновения, но проблематично в удобном пользователю виде выдать сообщение, содержащее тот вывод, который отправили в STDERR «запнувшиеся» команды. Также весьма неудобно то, что аргументом eval должна являться строка, обрамлённая либо двойными, либо одинарными кавычками: это приводит использованию довольно странных конструкций вида: Code: eval "echo \"$Name $Surname\"" eval 'echo "I'"'"'ve recently read '"'"'The store of Mabeyka'"'"' in esperanto"' Последнее выражение выглядит особенно уродливо, не так ли? Но BASH не был бы собой, если бы не позволял нам делать лёгкие и элегантные трюки полуджазовом стиле KISS. В качестве примера предлагаю потестировать простенькую функцию-обёртку для eval, которую написал я (буквально только что, «на коленке», в 3 часа ночи ): Code: try () { { STDERR=$( eval "$(cat -)" 3<&2 2>&1 1>&3 ); ret_=$?; } 2>&1 return ${ret_} } Заметьте, что функция try использует STDIN, а не параметры командной строки, так что мы запросто можем использовать конструкцию «Документ здесь», что не только сделает код более читабельным, органично встраивая его в основной поток исполнения скрипта, но и решает проблему со всеми видами кавычек. А вот как с помощью подобной функции можно выполнить код, непредвиденное завершение которого привело бы к неприятностям или даже к бессонной ночи вместо концерта «Animal ДжаZ»: Code: if ! try <<EOF psql <<<"SELECT pg_start_backup('$TIMESTAMP',true);" EOF then error_ "Could not initiate backup procedure, reason given by server:\n$STDERR" exit 1 fi Да, теперь мы можем справиться даже с тем кодом, который раньше казался совершенно неуправляемым: запряжём его eval'ом, чтобы не брыкался и в спокойной обстановке проанализируем причины возникновения ошибки/ошибок, изучив соотв. журнальный файл. Итак, давайте всё же попробуем это. Но на сей раз — осторожно, дабы не давать ошибкам ни малейшего шанса испортить вам настроение
Вот что у меня получилось, если кому то интересно.. Команды не нужно связывать через и &&, но они должны быть однострочными и код работает с конвеерами Code: try(){ while read -r line; do { ERR=$( eval $line 2>&1 1>&3 ); } 3>&1 || return 1 done } if ! try <<EOF ls /tmp | grep orb ls /ggg EOF then echo -e "Command: $line\n$ERR" fi
Вобщем такс... у кода в этой заметке были недостатки.. было невозможно пользоваться конвеерами и создавать переменные в блоке.. и вот, что получилось)) громоздко, но без ограничений и оказывается если делать перенаправление stdout из блока { ... }, то переменные не сохраняются Code: #! /bin/bash exec 3<> <( ) { ls -l \ /home ls /gg TST="MyVarib" ls /zzz echo EOF >&2 } 2>&3 while read a; do [[ "$a" == "EOF" ]] && break; ERR+="$a\n"; done <&3 if [[ "$ERR" != "" ]]; then echo -e "My Error: \n"$ERR fi echo $TST