Как отладить bash script?

Есть ли способ отладить bash script? Например, что-то, что печатает какой-то журнал выполнения, например "вызывающая строка 1", "вызывающая строка 2" и т.д.

Ответ 1

sh -x script [arg1 ...]
bash -x script [arg1 ...]

Это дает вам след того, что выполняется. (См. Также "Уточнение" в нижней части ответа.)

Иногда вам нужно управлять отладкой в ​​ script. В этом случае, поскольку Cheeto напомнил меня, вы можете использовать:

set -x

Это включает отладки. Затем вы можете отключить его с помощью:

set +x

(Вы можете узнать текущее состояние трассировки, проанализировав $-, текущие флаги, для x.)

Кроме того, оболочки обычно предоставляют опции "-n" для "без выполнения" и "-v" для режима "verbose"; вы можете использовать их в сочетании, чтобы узнать, может ли оболочка считать, что он может выполнить ваш script - иногда полезно, если у вас есть несбалансированная цитата где-то.


Существует утверждение, что опция < -x в Bash отличается от других оболочек (см. комментарии). Bash Manual говорит:

  • -x

    Распечатайте трассировку простых команд, команд for, команд case, команд select и арифметических команд for, а также их аргументов или связанных списков слов после их расширения и перед их выполнением. Значение переменной PS4 расширяется, и итоговое значение выводится перед командой и ее расширенными аргументами.

Это, по-видимому, не указывает на другое поведение вообще. Я не вижу никаких других ссылок на "-x" в руководстве. Он не описывает различия в последовательности запуска.

Уточнение: в таких системах, как типичный Linux-блок, где "/bin/sh" является символической ссылкой на "/bin/bash" (или везде, где найден исполняемый файл Bash), две командной строки достигают эквивалентного эффекта запустив script с трассировкой выполнения. В других системах (например, Solaris и некоторых более современных вариантах Linux) /bin/sh не Bash, и две командной строки будут давать (слегка) разные результаты. Прежде всего, "/bin/sh" будет путать конструкции в Bash, которые он вообще не распознает. (В Solaris, /bin/sh - оболочка Bourne, в современной Linux иногда Dash - меньшая, более строго оболочка POSIX). При вызове по имени, подобным этому, строка "shebang" ('#!/bin/bash' vs '#!/bin/sh ') в начале файла не влияет на интерпретацию содержимого.

В руководстве Bash есть раздел Bash POSIX mode, который, вопреки давней, но ошибочной версии этого ответа (см. также комментарии ниже), подробно описывает разницу между Bash, вызываемой как sh 'и Bash, вызываемой как bash'.

При отладке оболочки (Bash) script разумно и разумно - необходимо даже - использовать оболочку, названную в строке shebang, с опцией -x. В противном случае вы можете (будете?) Получать другое поведение при отладке при запуске script.

Ответ 2

Я использовал следующие методы для отладки моего script.

set -e немедленно отключает script, если какая-либо внешняя программа возвращает ненулевой статус выхода. Это полезно, если ваш script пытается обрабатывать все случаи ошибок и где отказ сделать это должен быть захвачен.

set -x упоминалось выше и, безусловно, является самым полезным из всех методов отладки.

set -n также может быть полезна, если вы хотите проверить свои ошибки в синтаксисе script.

strace также полезно посмотреть, что происходит. Особенно полезно, если вы не написали script самостоятельно.

Ответ 3

Этот ответ действителен и полезен: fooobar.com/questions/916/...

Но я нахожу, что методы "стандартного" script отладки неэффективны, неинтуитивны и сложны в использовании. Для тех, кто привык к сложным отладчикам GUI, которые ставят все на кончиках ваших пальцев и делают работу легкой задачей для простых проблем (и, возможно, для сложных проблем), эти решения не очень удовлетворительны.

Я использую комбинацию DDD и bashdb. Первый выполняет последний, а второй выполняет ваш script. Это обеспечивает многооконный пользовательский интерфейс с возможностью перехода через код в контекст и просмотр переменных, стека и т.д. Без постоянного умственного усилия по поддержанию контекста в вашей голове или повторному перечислению источника.

Есть рекомендации по настройке здесь: http://ubuntuforums.org/showthread.php?t=660223

Ответ 4

Вы также можете записать "set -x" в script.

Ответ 5

Я нашел утилиту shellcheck и, возможно, некоторые люди находят ее интересной https://github.com/koalaman/shellcheck

Маленький пример:

$ cat test.sh 
ARRAY=("hello there" world)

for x in $ARRAY; do
  echo $x
done

$ shellcheck test.sh 

In test.sh line 3:
for x in $ARRAY; do
         ^-- SC2128: Expanding an array without an index only gives the first element.

исправить ошибку, сначала попробуйте...

$ cat test.sh       
ARRAY=("hello there" world)

for x in ${ARRAY[@]}; do
  echo $x
done

$ shellcheck test.sh

In test.sh line 3:
for x in ${ARRAY[@]}; do
         ^-- SC2068: Double quote array expansions, otherwise they're like $* and break on spaces.

Повторите попытку...

$ cat test.sh 
ARRAY=("hello there" world)

for x in "${ARRAY[@]}"; do
  echo $x
done

$ shellcheck test.sh

найдите сейчас!

Это лишь небольшой пример.

Ответ 7

Установите VSCode, затем добавьте расширение bash debug, и вы готовы отлаживаться в визуальном режиме. см. Здесь в действии.

введите описание изображения здесь

Ответ 9

set + x = @ECHO OFF, установите -x = @ECHO ON.


Вы можете добавить опцию -xv к стандартному Shebang следующим образом:

#!/bin/bash -xv  

-x: отображать команды и их аргументы по мере их выполнения.
-v: отображать строки ввода оболочки во время их чтения.


ltrace - это еще одна утилита Linux, подобная strace. Тем не менее, ltrace перечисляет все вызовы библиотеки, вызываемые в исполняемом файле или запущенном процессе. Его имя происходит от трассировки библиотеки. Например:

ltrace ./executable <parameters>  
ltrace -p <PID>  

Источник

Ответ 10

Есть много подробностей о регистрации для скриптов оболочки через глобальные переменные оболочки. Мы можем эмулировать аналогичный вид ведения журнала в оболочке script: http://www.cubicrace.com/2016/03/log-tracing-mechnism-for-shell-scripts.html

Сообщение содержит информацию об интродуцированных уровнях журнала, таких как INFO, DEBUG, ERROR. Детали трассировки, такие как запись script, выход script, ввод функции, выход функции.

Пример журнала:

введите описание изображения здесь

Ответ 11

Некоторые трюки для отладки скрипты

Использование set -[nvx]

В дополнение к

set -x

и

set +x

для остановки дампа.

Я хотел бы поговорить о set -v, где дамп меньше, чем менее развитый вывод.

bash <<<$'set -x\nfor i in {0..9};do\n\techo $i\n\tdone\nset +x' 2>&1 >/dev/null|wc -l
21

for arg in x v n nx nv nvx;do echo "- opts: $arg"
    bash 2> >(wc -l|sed s/^/stderr:/) > >(wc -l|sed s/^/stdout:/) <<eof
        set -$arg
        for i in {0..9};do
            echo $i
          done
        set +$arg
        echo Done.
eof
    sleep .02
  done
- opts: x
stdout:11
stderr:21
- opts: v
stdout:11
stderr:4
- opts: n
stdout:0
stderr:0
- opts: nx
stdout:0
stderr:0
- opts: nv
stdout:0
stderr:5
- opts: nvx
stdout:0
stderr:5

Переменные дампа или трассировка на лету

Для тестирования некоторых переменных я иногда использую это:

bash <(sed '18ideclare >&2 -p var1 var2' myscript.sh) args

для добавления:

declare >&2 -p var1 var2

в строке 18 и запуская результат script (с аргументами), без необходимости редактировать их.

конечно, это можно было бы использовать для добавления set [+-][nvx]:

bash <(sed '18s/$/\ndeclare -p v1 v2 >\&2/;22s/^/set -x\n/;26s/^/set +x\n/' myscript) args

добавит declare -p v1 v2 >&2 после строки 18, set -x до строки 22 и set +x до строки 26.

маленький образец:

bash <(sed '2,3s/$/\ndeclare -p LINENO i v2 >\&2/;5s/^/set -x\n/;7s/^/set +x\n/' <(
        seq -f 'echo [email protected], $((i=%g))' 1 8)) arg1 arg2
arg1 arg2, 1
arg1 arg2, 2
declare -i LINENO="3"
declare -- i="2"
/dev/fd/63: line 3: declare: v2: not found
arg1 arg2, 3
declare -i LINENO="5"
declare -- i="3"
/dev/fd/63: line 5: declare: v2: not found
arg1 arg2, 4
+ echo arg1 arg2, 5
arg1 arg2, 5
+ echo arg1 arg2, 6
arg1 arg2, 6
+ set +x
arg1 arg2, 7
arg1 arg2, 8

Примечание. Уход за $LINENO будет зависеть от изменений "на лету"!

(Чтобы увидеть результат script без выполнения, просто снимите bash <( и ) arg1 arg2)

Шаг за шагом, время выполнения

Посмотрите мой ответ о том, как профилировать bash скрипты

Ответ 12

Используйте eclipse с обтекаемыми плагинами и baschclipse.

https://sourceforge.net/projects/shelled/?source=directory https://sourceforge.net/projects/basheclipse/?source=directory

Для обледенения: загрузите zip и импортируйте его в eclipse с помощью справки → установите новое программное обеспечение: локальный архив. Для basheclipse: скопируйте банки в каталог распаковки eclipse

Следуйте инструкциям https://sourceforge.net/projects/basheclipse/files/?source=navbar

введите описание изображения здесь

Я написал учебник со многими снимками экрана http://dietrichschroff.blogspot.de/2017/07/bash-enabling-eclipse-for-bash.html

Ответ 13

Запись сообщений об ошибках Bash

Скрипт оболочки представляет собой текстовый файл, содержащий команды оболочки. По умолчанию интерпретатор команд и утилиты оболочки отображают сообщения об ошибках и диагностические сообщения при стандартной ошибке. Bash может печатать трассировку работающего скрипта, если включена опция xtrace.

Пример кода печатает трассировку в файле xtrace.log. Функция err позволяет быстро идентифицировать сообщения об ошибках. Трассировка функции err отменяется, поскольку ее текущая среда выполнения скрывает информацию, которая формирует трассировку.

Образец кода

#!/bin/bash
set -o errexit

exec 4>xtrace.log
BASH_XTRACEFD=4
PS4='+ $0: line ${LINENO}: ${FUNCNAME[0]}: '
set -x

echo 'Welcome!'

err()
{
   printf "%s\n" "${BASH_SOURCE[0]}: line ${BASH_LINENO[0]}: ${FUNCNAME[0]}: $1"
} >&2 4>/dev/null

err 'oops, something went wrong!'

Этот пример кода выводит:

prompt% my_script
Welcome
./my_script: line 16: err: oops, something went wrong!

Сообщения об ошибках могут быть перенаправлены, чтобы скрыть диагностические сообщения:

prompt% my_script 2>err.log
Welcome!

Посмотрите на сообщение об ошибках слежения и просмотра и диагностические сообщения:

prompt% cat xtrace.log
+ ./my_script: line 9: main: echo Welcome
+ ./my_script: line 16: main: err 'oops, something went wrong!'
prompt% cat err.log
./my_script: line 16: err: oops, something went wrong!