Новости из Блогов BASH Tips&Tricks #000C: Lets try it now!

Discussion in 'Мировые новости. Обсуждения.' started by zeks1, 9 Apr 2013.

  1. zeks1

    zeks1 New Member

    Joined:
    11 Jan 2013
    Messages:
    73
    Likes Received:
    0
    Reputations:
    1
    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'ом, чтобы не брыкался и в спокойной обстановке проанализируем причины возникновения ошибки/ошибок, изучив соотв. журнальный файл.

    Итак, давайте всё же попробуем это. Но на сей раз — осторожно, дабы не давать ошибкам ни малейшего шанса испортить вам настроение ;)
     
  2. taha

    taha Elder - Старейшина

    Joined:
    20 Aug 2006
    Messages:
    399
    Likes Received:
    330
    Reputations:
    251
    Вот что у меня получилось, если кому то интересно.. Команды не нужно связывать через и &&, но они должны быть однострочными и код работает с конвеерами
    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
     
  3. taha

    taha Elder - Старейшина

    Joined:
    20 Aug 2006
    Messages:
    399
    Likes Received:
    330
    Reputations:
    251
    Вобщем такс... у кода в этой заметке были недостатки.. было невозможно пользоваться конвеерами и создавать переменные в блоке..

    и вот, что получилось)) громоздко, но без ограничений
    и оказывается если делать перенаправление 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