Введение
Мне всегда хотелось иметь возможность отлаживать 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.
Пример
Комментарии (26)
garbagecollected
21.05.2022 07:45+15А можно просто отлаживать скрипты включив режим отладки bash
set -x
Или можно совместить с холостым выполнением bash
set -n
prefrontalCortex
21.05.2022 10:37+3Я искренне думал, что эта статья сведётся к строчке
set -euxo pipefail
tminnigaliev Автор
21.05.2022 10:59+1К сожалению, опция -x не даёт отладки по шагам, а лишь печатает то, что выполняется. Кому-то удобно, кому-то нет. Я принципиально не стал освещать разные опции баша, чтоб не отвлекать от основной идеи.
Про опцию -n я не слышал. Спасибо, попробую.
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.
tminnigaliev Автор
22.05.2022 11:18Было бы круто! Про readline я пытался начинать читать, но как-то, видимо, не в то время.
По поводу линейности скриптов - полностью поддерживаю. Лично я отлаживал сценарии плдготовки релиза - скрипт, который на ветках запускает тесты, мержит с веток в мастер, запускает тесты на мастере и добавляет метку релиза, но не пушит, пушу я руками, если всё ок.
Т.е. скрипт прямолинейный и простой. И пользуюсь им только я.
capslocky
22.05.2022 21:19Недавно мне надо было написать скрипт для docker alpine и я так и не смог нагуглить какой-нибудь нормальный cheat sheet для sh, так как в выдаче поиска был один только bash. Пришлось добавить
apk add --no-cache bash
.Кто-нибудь может поделиться ссылкой на удобный справочник по sh?
randomsimplenumber
21.05.2022 08:01+1bashdb ?
tminnigaliev Автор
21.05.2022 09:46С bashdb надо заморачиваться, нет его в "базовой комплектации". А работать иногда приходится на серверах, где у меня нет админских прав.
randomsimplenumber
21.05.2022 10:17+1Ну, права на $HOME есть же? Закинуть заранее собранный bashdb .
Хотя, если bashdb действительно необходим - значит что то делается совсем неправильно. :)
miga
21.05.2022 18:14+4Если ваш скрипт надо отлаживать, то его стоило бы написать на нормальном языке
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 после каждой строчки) более похожим на нормальную отладку" описанные в статье :)
tminnigaliev Автор
21.05.2022 20:25Про плагин для vscode не знал. В хорошее время мы живём... ничего делать не надо - надо просто найти то, что уже кем-то сделано до нас.
memclutter
21.05.2022 22:15+3Использование плагина vscode ограничивает выбор окружения, отладить скрипт на сервере уже не получится.
Rosik
21.05.2022 23:58Отлаживать неотлаженные скрипты на проде - не лучшая затея.
eps
22.05.2022 00:28+3Удалённый тестовый сервер. Или удалённая one-shot disposable machine для проверки теории. Часто приходится работать с таким
randomsimplenumber
22.05.2022 08:10В тестовый сервер можно и отладчик установить, и vscode со всеми плагинами.
selivanov_pavel
Отличная идея! Можно ещё позволить выполнять команды перед каждой строкой:
А ещё можно сделать скрипт, который будет всё это делать для другого скрипта.
Пожалуй причешу и утащу в блог, с указанием авторства оригинала, разумеется.
kt97679
Более того, в рамках этого решения еще и breakpoint-ы можно реализовать.
tminnigaliev Автор
У меня всё и началось именно с них - я использовал read в этом качестве, добавляя его везде, где хотелось, чтоб была точка останова. Эдакий аналог Octave-ского keyboard.
kt97679
Добавлять read для останова не очень удобно. Я имел в виду, что при вызове _debug_for_bash можно проверять номер строки на которой сработал trap. И запускать цикл с read только если этот номер находится в списке breakpoint-ов. В цикле перед eval надо добавить обработку команд на добавление и удаление breakpoint-ов.
tminnigaliev Автор
Ну вот это может оказаться очень тяжеловесно... Если @selivanov_pavel сделает в виде отдельного скрипта, тогда и хорошо. А если таскать из скрипта в скрипт много строк кода, тогда, возможно, и не стоит.. впрочем, тут дело вкуса и того, как оно будет реализовано. Не готов заранее оценивать юзабельность этого.
tminnigaliev Автор
Вот спасибо хорошо! Я пытался подобное сделать, но моего баш кунг фу в сочетании с тем, что было достаточно приведённого решения, не хватило для того, чтоб закончить.
Когда напишете в блог, пожалуйста, пришлите ссылку, я её добавлю сюда, чтоб те, кто находят эту статью, могли и на вашу быстро перейти. Или сами добавьте, пожалуйста.
selivanov_pavel
Пост: https://selivan.github.io/2022/05/21/bash-debug.html
Скриптик для отладки других скриптов: https://github.com/selivan/bash-debug
Healer
https://selivan.github.io/2022/05/21/bash-debug.html