Как отлаживать bash-script-ы по шагам или, возможно, самая короткая статья о программировании/отладке на Хабре +158


Введение

Мне всегда хотелось иметь возможность отлаживать bash-scripts так же, как и любой другой код, т.е. по шагам, и bash такую возможность предусмотрел, но о ней не все знают. Несмотря на довольно большой опыт использования Linux, я дошёл до неё только недавно.

Волшебная строчка, которую нужно добавить после #!/bin/bash, чтоб скрипт можно было отлаживать по шагам

#!/bin/bash

trap 'echo "# $BASH_COMMAND";read' DEBUG

echo line1
echo line2

echo line3

Процесс отладки

Запускаем скрипт, перед выполнением каждой команды выводится то, что будет исполняться, затем интерпретатор начинает ожидать нажатия клавиши <ENTER>.

Если понимаем, что что-то пошло не так, нажимаем Ctrl+C и выходим из отладки.

Ингредиенты

  • команда trap, которая умеет перехватывать разные сигналы и в нашем случае она перехватывает сигнал DEBUG, посылаемый перед выполнением команды

  • команда read, которая умеет ожидать ввода чего-нибудь с клавиатуры (в данном случае нам нужно только либо ENTER либо Ctrl+C

  • переменная окружения $BASH_COMMAND, валидная внутри обработчика команды trap.

Пример

https://onlinegdb.com/1h1BpiINv




Комментарии (26):

  1. selivanov_pavel
    /#24363240 / +26

    Отличная идея! Можно ещё позволить выполнять команды перед каждой строкой:

    function _debug_for_bash() {
        echo "# $BASH_COMMAND";
        while read -p "debug> " _cmnd; do
            if [ -n "$_cmnd" ]; then
                eval "$_cmnd";
            else
                break;
            fi;
        done
    }
    
    trap '_debug_for_bash' DEBUG
    

    А ещё можно сделать скрипт, который будет всё это делать для другого скрипта.

    Пожалуй причешу и утащу в блог, с указанием авторства оригинала, разумеется.

    • kt97679
      /#24363362 / +1

      Более того, в рамках этого решения еще и breakpoint-ы можно реализовать.

      • tminnigaliev
        /#24363606

        У меня всё и началось именно с них - я использовал read в этом качестве, добавляя его везде, где хотелось, чтоб была точка останова. Эдакий аналог Octave-ского keyboard.

        • kt97679
          /#24364082

          Добавлять read для останова не очень удобно. Я имел в виду, что при вызове _debug_for_bash можно проверять номер строки на которой сработал trap. И запускать цикл с read только если этот номер находится в списке breakpoint-ов. В цикле перед eval надо добавить обработку команд на добавление и удаление breakpoint-ов.

          • tminnigaliev
            /#24364226

            Ну вот это может оказаться очень тяжеловесно... Если @selivanov_pavel сделает в виде отдельного скрипта, тогда и хорошо. А если таскать из скрипта в скрипт много строк кода, тогда, возможно, и не стоит.. впрочем, тут дело вкуса и того, как оно будет реализовано. Не готов заранее оценивать юзабельность этого.

  2. garbagecollected
    /#24363248 / +15

    А можно просто отлаживать скрипты включив режим отладки bash

    set -x

    Или можно совместить с холостым выполнением bash

    set -n

    • prefrontalCortex
      /#24363438 / +3

      Я искренне думал, что эта статья сведётся к строчке

      set -euxo pipefail

    • tminnigaliev
      /#24363470 / +1

      К сожалению, опция -x не даёт отладки по шагам, а лишь печатает то, что выполняется. Кому-то удобно, кому-то нет. Я принципиально не стал освещать разные опции баша, чтоб не отвлекать от основной идеи.

      Про опцию -n я не слышал. Спасибо, попробую.

      • garbagecollected
        /#24365112 / +5

        Всё ниже указанное стоит рассматривать как мой личный опыт, и моё мнение может не совпадать с мнением коллег, потому не стоит воспринимать как истину последней инстанции.

        С bash я работаю плотно с 2009 года. За это время я имел опыт работы с perl, python, awk, sed, jq, bc, dc, grep и прочими типичными инструментами связанными с работой в эмуляторах терминала. На протяжении этого периода стиль моего написания скриптов менялся несколько раз. На данный момент те задачи, которые я решаю с помощью bash, не требуют контроля пошагового выполнения, и этому есть три причины.

        Во-первых, из всех написанных мною скриптов, используемые по сей день скрипты отличаются прямолинейностью алгоритма. По мере получения опыта я стараюсь сводить к минимуму использования условных операторов, циклов и других элементов контроля выполнения. Например, по циклам предел вложенности в моих скриптах очень редко достигает трёх циклов, обычно не больше двух. Если задача требует более сложных структур, я разделяю её на несколько маленьких скриптиков. Я предпочитаю именно несколько скриптиков в отдельных файлах вместо использования функций в одном файле. Если задача требует алгоритмов из алгоритмизации или, не дай бог, вычислительной математики. Например, для поиска всех пересечений двух кубических кривых безье, вместо bash я отдал бы предпочтение чему-либо другому. И даже дело не в том, что bash скуден на инструментарий решения математики. Объединяя средства bash, perl, awk, grep мощность обработки строк и манипуляций с текстовыми или полу-текстовыми потоками с лихвой компенсирует слабую математическую сторону bash. Простые скрипты с линейным алгоритмом на bash хочется использовать снова и снова. А когда в скрипте десятки точек выхода, рекурсивных вызовов, бесконечных циклов, такие скрипты пишутся на один раз. А потом, открыв такой скрипт даже вспоминать не хочется, что сам там накодил.

        Во-вторых, не смотря на линейность алгоритма, скрипты могут быть большими, и их может быть много. Например, рабочая сборка livecd содержит более 250 пакетов из AUR. Это же сколько тысяч раз мне надо нажать на Enter, пока сформируется репозиторий с актуальными версиями рабочего софта!

        В-третьих, если запустить bash без параметров, он как раз откроется в похожем режиме. Бонусом этого режима является возможность контролировать ход выполнения скрипта, задавая новые значения переменным и редактируя команды скрипта. Побочным эффектом такого подхода будет необходимость искусно манипулировать историей команд и реверсивным поиском. Ну, или копировать-вставлять в окно эмулятора терминала по-командно. Тоже опыт. Кстати, именно в таком режиме я веду разработку особо длинных команд bash.

        Завершить свой монолог хочу действительно правильным советом и напутствием. Пишите скрипты на bash только для собственного использования на подконтрольных вам компьютерах и серверах. А когда будете писать скрипты с расчетом того, что их будут запускать другие люди на других разных компьютерах, такие скрипты пишите на sh. Да, там нет того синтаксического сахара bash. Скрипты на sh пишутся чуть дольше и чуть длиннее. Но это время окупится. Не сомневайтесь! Коллеги в чужих скриптах разбираться не любят. Скинут вывод ошибки, которую вы часами будете пытаться воспроизвести. А в итоге выяснится, что запускают скрипт на каком-нибудь osx или каким-нибудь древним busybox. Рано или поздно все приходят к sh. Back to the primitive (c)Soulfly. А для своих нужд я лично уже наверное года 4 пользуюсь fish. Пробовал zsh - мне лично не зашёл. У fish лучший autocomplete по клавише Tab прямо из коробки. Но скрипты все равно пишу на bash/sh.

        Также ещё хочется дать напутствие. Попробуйте разобраться как работает readline. Вы им пользуетесь каждый рабочий день, но никогда не задумывались о нём. Этот чёрный ящик линукса вообще мало кто любит трогать. В коде так почти ничего не поменяли с 90х годов. Попробуйте, например, поменять сигналы на другие комбинации клавиш, а Ctrl+x забиндить вырезать, Ctrl+c - скопировать, Ctrl+v - вставить, Ctrl+z - отменить, Ctrl+a - выделить всё. К тому моменту когда у вас получится это сделать, вы будете гораздо больше понимать в терминалах и, собственно, почему их эмулируют. Эх, может через пару-тройку лет настанет день, и я напишу огромнейший пост про readline.

        • tminnigaliev
          /#24365500

          Было бы круто! Про readline я пытался начинать читать, но как-то, видимо, не в то время.

          По поводу линейности скриптов - полностью поддерживаю. Лично я отлаживал сценарии плдготовки релиза - скрипт, который на ветках запускает тесты, мержит с веток в мастер, запускает тесты на мастере и добавляет метку релиза, но не пушит, пушу я руками, если всё ок.

          Т.е. скрипт прямолинейный и простой. И пользуюсь им только я.

        • capslocky
          /#24366968

          Недавно мне надо было написать скрипт для docker alpine и я так и не смог нагуглить какой-нибудь нормальный cheat sheet для sh, так как в выдаче поиска был один только bash. Пришлось добавить apk add --no-cache bash.

          Кто-нибудь может поделиться ссылкой на удобный справочник по sh?

    • phikus
      /#24364814

      Без настроенного PS4 этого мало для эффективной отладки

  3. randomsimplenumber
    /#24363270 / +1

    bashdb ?

    • tminnigaliev
      /#24363364

      С bashdb надо заморачиваться, нет его в "базовой комплектации". А работать иногда приходится на серверах, где у меня нет админских прав.

      • randomsimplenumber
        /#24363400 / +1

        Ну, права на $HOME есть же? Закинуть заранее собранный bashdb .

        Хотя, если bashdb действительно необходим - значит что то делается совсем неправильно. :)

  4. miga
    /#24364132 / +4

    Если ваш скрипт надо отлаживать, то его стоило бы написать на нормальном языке

  5. Karroplan
    /#24364324 / +1

    есть bash script debug extension для VSCode и там реализованы все нужные вещи - step over, step in, breakpoints и т.п.:

    https://marketplace.visualstudio.com/items?itemName=rogalmic.bash-debug

    еще есть в природе bash debug project для любителей консоли:

    http://bashdb.sourceforge.net/

    имея эти две вещи можно забыть про извращения вида "как еще сделать отладочный вывод (prinf после каждой строчки) более похожим на нормальную отладку" описанные в статье :)

    • tminnigaliev
      /#24364358

      Про плагин для vscode не знал. В хорошее время мы живём... ничего делать не надо - надо просто найти то, что уже кем-то сделано до нас.

    • memclutter
      /#24364524 / +3

      Использование плагина vscode ограничивает выбор окружения, отладить скрипт на сервере уже не получится.

      • Rosik
        /#24364708

        Отлаживать неотлаженные скрипты на проде - не лучшая затея.

        • eps
          /#24364768 / +3

          Удалённый тестовый сервер. Или удалённая one-shot disposable machine для проверки теории. Часто приходится работать с таким

          • randomsimplenumber
            /#24365102

            В тестовый сервер можно и отладчик установить, и vscode со всеми плагинами.