Введение

Мне всегда хотелось иметь возможность отлаживать 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
    21.05.2022 07:27
    +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
    

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

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


    1. kt97679
      21.05.2022 09:45
      +1

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


      1. tminnigaliev Автор
        21.05.2022 12:13

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


        1. kt97679
          21.05.2022 17:39

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


          1. tminnigaliev Автор
            21.05.2022 19:21

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


    1. tminnigaliev Автор
      21.05.2022 09:51
      +1

      Вот спасибо хорошо! Я пытался подобное сделать, но моего баш кунг фу в сочетании с тем, что было достаточно приведённого решения, не хватило для того, чтоб закончить.

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


      1. selivanov_pavel
        21.05.2022 14:50
        +2

        Пост: https://selivan.github.io/2022/05/21/bash-debug.html

        Скриптик для отладки других скриптов: https://github.com/selivan/bash-debug



  1. garbagecollected
    21.05.2022 07:45
    +15

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

    set -x

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

    set -n


    1. prefrontalCortex
      21.05.2022 10:37
      +3

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

      set -euxo pipefail


    1. tminnigaliev Автор
      21.05.2022 10:59
      +1

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

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


      1. garbagecollected
        22.05.2022 08:34
        +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.


        1. tminnigaliev Автор
          22.05.2022 11:18

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

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

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


        1. capslocky
          22.05.2022 21:19

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

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


    1. phikus
      22.05.2022 01:02

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


  1. randomsimplenumber
    21.05.2022 08:01
    +1

    bashdb ?


    1. tminnigaliev Автор
      21.05.2022 09:46

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


      1. randomsimplenumber
        21.05.2022 10:17
        +1

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

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


  1. miga
    21.05.2022 18:14
    +4

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


  1. Karroplan
    21.05.2022 20:00
    +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 после каждой строчки) более похожим на нормальную отладку" описанные в статье :)


    1. tminnigaliev Автор
      21.05.2022 20:25

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


    1. memclutter
      21.05.2022 22:15
      +3

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


      1. Rosik
        21.05.2022 23:58

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


        1. eps
          22.05.2022 00:28
          +3

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


          1. randomsimplenumber
            22.05.2022 08:10

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


      1. NetherQuartz
        23.05.2022 11:49

        VS Code умеет открывать SSH сессии