ИТ Блог. Администрирование серверов на основе Linux (Ubuntu, Debian, CentOS, openSUSE)

5 простых шагов по отладке сценария оболочки Bash

5 простых шагов по отладке сценария оболочки Bash

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

В этом посте рассматриваются различные специальные свойства оболочки Bash и то, как вы можете использовать их для эффективной отладки скриптов.

 

3 типа ошибок скрипта

Отладка скрипта оболочки Bash иногда может быть утомительной. Выполнив 5 простых шагов в этом посте, вы избавите себя от некоторых серьезных головных болей.

При программировании существует три основных типа ошибок, и это остается верным в сценариях оболочки.

  1. Синтаксическая ошибка
    Обычно синтаксические ошибки легче всего устранить в сценарии Bash. Их часто можно обнаружить без выполнения сценария оболочки. К наиболее распространенным синтаксическим ошибкам относятся:
    • Неправильное использование квадратных скобок в операторе If
    • Неправильный синтаксис при использовании цикла Bash или массива Bash
    • Неправильное использование составной команды при определении функции Bash
  2. Ошибка во время выполнения
    Ошибка во время выполнения будет следующим уровнем ошибок. Сценарий оболочки выполняется без синтаксических ошибок, но не может надежно выполнять определенные задачи. Наиболее распространенные ошибки во время выполнения сценария оболочки включают:
    • Деление на ноль или использование переменной string/float в арифметическом выражении Bash
    • Неверный нижний индекс при динамическом заполнении ассоциативного массива Bash
    • Неправильный синтаксический анализ вывода файла или команды, например, при обработке CSV-файла в Bash или подсчете файлов в каталоге
    • Запуск с неправильными разрешениями sudo или настройками tty
  3. Логическая ошибка
    Логические ошибки часто сложнее всего устранить, поскольку они связаны с недостатками дизайна и логическими ошибками. Отладка таких ошибок может быть затруднена, если не иметь полного понимания сквозного выполнения скрипта и ожидаемого поведения. К наиболее распространенным логическим ошибкам в сценарии оболочки относятся:
    • Неправильное использование оператора test в условном операторе, например, using -z вместо -n в условии if
    • Неправильное использование арифметического оператора, такого как умножение, вместо ввода числа
    • Неправильное условие для выхода из цикла Bash, например, при использовании while цикла вместо цикла until
    • Неправильное манипулирование датами Linux с разными часовыми поясами
    • Отображение неправильного сообщения в командной строке или в журналах

 

5 шагов по отладке скрипта в Bash

Шаг 1: Используйте согласованную библиотеку отладки

Какова бы ни была цель ваших сценариев оболочки, работаете ли вы в одиночку или сотрудничаете с товарищами по команде, важно быть последовательным и систематичным в процессе отладки, чтобы быстро устранять неполадки и отлаживать сценарий оболочки.

Вы можете систематически создавать исходные файлы общей библиотеки, которая содержит все ваши настройки отладки, используя команды source и dot, но этот подход не идеален, если вы унаследовали чужую проблему и не хотите или не можете изменять исходный код.

Альтернативой является использование переменной окружения $BASH_ENV, которая используется Bash для определения файла инициализации для чтения перед выполнением скрипта. Вы можете использовать его для создания среды для целей отладки и определения конкретных параметров оболочки или отладочных ловушек.

Переменная $ENV аналогична $BASH_ENV. Она используется, когда оболочка запускается в режиме совместимости с POSIX.

### Определите среду отладки
### Filename: my-debug-env
trap 'echo "$BASH_COMMAND" failed with error code $?' ERR

#!/usr/bin/env bash
### пример Script
### Filename: example-debug

echo "Пример Script..."
bad_command &> /dev/null

### Пример вывода без debug env
[andreyex@linux ~]$ ./example-debug 
Example Script...

### Пример вывода с помощью debug env
[andreyex@linux ~]$ BASH_ENV=./my-debug-env ./example-debug
Example Script...
bad_command &> /dev/null failed with error code 127

 

На следующих шагах мы продолжим улучшать содержимое BASH_ENV скрипта инициализации ./my-debug-env. Преимущество этого метода в том, что независимо от того, какой скрипт вы собираетесь отлаживать и на каком сервере Linux вы находитесь, у вас будут согласованные настройки, ловушки и ведение журнала (если только это не переопределено самим скриптом).

 

Шаг 2: Проверьте, нет ли синтаксической ошибки

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

Давайте рассмотрим простую, но распространенную синтаксическую ошибку, связанную с неправильным использованием единственной квадратной скобки и пропущенным then ключевым словом.

#!/usr/bin/env bash
# Filename: ./example-syntax-error
echo "Это было выполнено"
if [-z "$v" ]; 
 echo "Здесь опечатка"
fi

## Output
[andreyex@linux ~]$ ./example-syntax-error
Это было выполнено
./test: line 12: syntax error near unexpected token `fi'
./test: line 12: `fi'

 

В Bash есть две опции для обнаружения синтаксических ошибок такого рода без выполнения сценария. Во-первых, с помощью опции оболочки noexec. Во-вторых, с помощью инструмента статического анализа.

 

Используйте параметр оболочки noexec

Оболочка Bash предоставляет noexec параметр оболочки, который может быть установлен либо с помощью set -o noexec, либо с помощью set -n. Этот параметр заставит оболочку Bash только читать команды, но не выполнять их, а также не будет выполнять присвоение переменной. Короче говоря, это базовый режим “без операции“ или “пробный запуск”.

⚠️ Как только noexec опция выполняется set встроенной командой, любые другие команды после этого НЕ будут выполняться, включая любую следующую команду set.

 

Вы можете легко включить небольшое условие и использовать переменную окружения, чтобы автоматически включить опцию noexec проверки синтаксиса вашего скрипта. Это можно сделать непосредственно в исходном коде вашего скрипта или, что лучше, в сценарии инициализации среды отладки, my-debug-env как упоминалось на шаге 1, используя переменную BASH_ENV. Это может быть удобно в конвейере CI/CD перед развертыванием вашего сценария в рабочей среде.

### Определите среду отладки
### Filename: my-debug-env
if [[ -v NOOP ]]; then
  echo "Run NOOP mode"
  set -o noexec # same as set -n
fi
trap 'echo "$BASH_COMMAND" failed with error code $?' ERR

#!/usr/bin/env bash
# Filename: example-syntax-error
echo "Это было выполнено"
if [-z "$v" ]; 
 echo "Здесь опечатка"
fi

## Output
[andreyex@linux ~]$ NOOP=1 BASH_ENV=my-debug-env ./example-syntax-error
Run NOOP mode
./example-syntax-error: line 6: syntax error near unexpected token `fi'
./example-syntax-error: line 6: `fi'

 

Теперь вы можете получить сообщение о синтаксической ошибке оболочки без выполнения какой-либо из команд в скрипте.

👉 Этот метод не сможет перехватить синтаксические ошибки в сценарии оболочки, импортированном с помощью команд source или dot, поскольку команды не будут выполняться в режиме noexec. Каждый скрипт следует проверять индивидуально на наличие синтаксических ошибок.

 

Используйте инструмент статического анализа: ShellCheck

Одна из проблем с noexec режимом заключается в том, что он просто выводит необработанные синтаксические ошибки, которые не всегда легко интерпретировать и диагностировать. Лучшим подходом является использование инструмента статического анализа, такого как ShellCheck, который стал стандартом defacto, когда дело доходит до статического анализа скриптов Bash.

Наш предыдущий пример с синтаксической ошибкой shellcheck предоставляет нам гораздо более понятную информацию и полезные рекомендации по исправлению ошибок.

[andreyex@linux ~]$ shellcheck ./example-syntax-error 

In ./example-syntax-error line 9:
if [-z "$v" ]; 
^-- SC1049: Did you forget the 'then' for this 'if'?
^-- SC1073: Couldn't parse this if expression. Fix to allow more checks.
   ^-- SC1035: You need a space after the [ and before the ].


In ./example-syntax-error line 11:
fi
^-- SC1050: Expected 'then'.
  ^-- SC1072: Unexpected keyword/token. Fix any mentioned problems and try again.

For more information:
  https://www.shellcheck.net/wiki/SC1035 -- You need a space after the [ and ...
  https://www.shellcheck.net/wiki/SC1049 -- Did you forget the 'then' for thi...
  https://www.shellcheck.net/wiki/SC1050 -- Expected 'then'.

 

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

[andreyex@linux ~]$ shellcheck ./example-syntax-error 

In example-xtrace line 9:
if [[ -z "$v" ]]; then
          ^-- SC2154: v is referenced but not assigned.

For more information:
  https://www.shellcheck.net/wiki/SC2154 -- v is referenced but not assigned.

 

👉 Вы можете многое сделать с shellcheck, чтобы уменьшить количество ошибок в ваших скриптах оболочки bash и ускорить отладку. Обычно мы будем явно использовать следующий синтаксисshellcheck -s bash -o all -xa <your_script>, чтобы воспользоваться всеми возможными рекомендациями (опция -o all) и убедиться, что исходные файлы также проверены (опции -xa).

 

Шаг 3: Отследите выполнение команды вашего скрипта

Еще одна полезная опция оболочки – это xtrace опция, которую можно включить с помощью set -o xtrace или set -x. Когда ваш скрипт Bash выполняется в режиме трассировки, все команды и их аргументы распечатываются перед выполнением. Это полезно для отладки ошибок среды выполнения и логики в скрипте.

📎 Вы можете заменить + символ по умолчанию, используемый в выводе xtrace, изменив переменную Bash Prompt $PS4.

 

В качестве альтернативы вы можете использовать verbose опцию shell вместо этого, используя set -o verbose или set -v. Основное отличие от xtrace заключается в том, что подробный режим отображает строки ввода оболочки только по мере их чтения. Вы не увидите аргументы, передаваемые командам, что обычно полезно при попытке отладки сценария bash. Возможно, вам будет полезно использовать оба варианта.

### Определите среду отладки
### Filename: my-debug-env
if [[ -v TRACE ]]; then
  echo "Run TRACE mode"
  set -o xtrace # same as set -x
fi
if [[ -v NOOP ]]; then
  echo "Run NOOP mode"
  set -o noexec # same as set -n
fi
trap 'echo "$BASH_COMMAND" failed with error code $?' ERR

#!/usr/bin/env bash
# Filename: ./example-xtrace
echo "Это было выполнено"
v=$1
if [[ -z "${v}" ]]; then
 echo "\$v is empty"
fi

## Output
[andreyex@linux ~]$ NOOP=1 TRACE=1 BASH_ENV=my-debug-env ./example-xtrace 
Run TRACE mode
+ [[ -v NOOP ]]
+ echo 'Run NOOP mode'
Run NOOP mode
+ set -o noexec

[andreyex@linux ~]$ TRACE=1 BASH_ENV=my-debug-env ./example-xtrace 
Run TRACE mode
+ [[ -v NOOP ]]
+ echo 'Это было выполнено'
Это было выполнено
+ [[ -z '' ]]
+ echo '$v is empty'
$v is empty

 

Такой вывод трассировки хорош, но он может быстро усложнить отладку скрипта, поскольку весь вывод скрипта путается в терминале. Простым и очевидным решением является перенаправление стандартных ошибок в другой файл с помощью, например TRACE=1 ./example-xtrace 2>error.log. Однако, если ваш скрипт использует стандартный вывод ошибок, то у вас все равно остается трассировка отладки, смешанная со стандартными ошибками.

Чтобы решить эту проблему, Bash может записать выходные данные трассировки, сгенерированные опцией xtrace оболочки, в заданный файловый дескриптор, указанный в переменной $BASH_XTRACEFD. Это позволяет отделить вывод трассировки от стандартного вывода скрипта и стандартных сообщений об ошибках. В нашем следующем примере используется файл xtrace.out, но вместо этого вы можете использовать перенаправление оболочки на утилиту ведения журнала, такую как logger или syslog.

Файловый дескриптор закрывается, когда переменная $BASH_XTRACEFD отменяется или ей присваивается новое значение. Отмена установки $BASH_XTRACEFD или присвоение ей пустой строки приводит к тому, что вывод трассировки отправляется со стандартной ошибкой.

⚠️ Установка $BASH_XTRACEFD стандартного дескриптора файла ошибки (2), а затем его отмена приведет к закрытию стандартной ошибки.

[andreyex@linux ~]$ lsof -p $$ # List all current open files
...
[andreyex@linux ~]$ ls /proc/$$/fd # Use /dev/fd/ on mac
0 1 2 255
[andreyex@linux ~]$ exec 4>xtrace.out # Could be replaced by: exec 4> >(logger -t $0)
[andreyex@linux ~]$ ls /proc/$$/fd
0 1 2 255 4
[andreyex@linux ~]$ BASH_XTRACEFD=4
[andreyex@linux ~]$ set -x
[andreyex@linux ~]$ less xtrace.out
++ update_terminal_cwd
++ local url_path=
++ local i ch hexch LC_CTYPE=C LC_ALL=
++ (( i = 0 ))
...
[andreyex@linux ~]$ set +x
[andreyex@linux ~]$ unset BASH_XTRACEFD
[andreyex@linux ~]$ ls /proc/$$/fd
0 1 2 255

 

Затем наш предыдущий пример можно обновить, чтобы использовать переменную BASH_XTRACEFD при включенной трассировке.

### Определите среду отладки
### Filename: my-debug-env
if [[ -v TRACE ]]; then
  echo "Run TRACE mode"
  exec 4>./xtrace.out # Could be replaced by: exec 4> >(logger -t $0)
  BASH_XTRACEFD=4
  set -o xtrace # same as set -x
fi
if [[ -v NOOP ]]; then
  echo "Run NOOP mode"
  set -o noexec # same as set -n
fi
trap 'echo "$BASH_COMMAND" failed with error code $?' ERR

#!/usr/bin/env bash
# Filename: ./example-xtrace
echo "Это было выполнено"
v=$1
if [[ -z "${v}" ]]; then
 echo "\$v is empty"
fi

## Output
[andreyex@linux ~]$ TRACE=1 BASH_ENV=my-debug-env ./example-xtrace 
Run TRACE mode
Это было выполнено
$v is empty
[andreyex@linux ~]$ cat xtrace.out 
+ [[ -v NOOP ]]
+ echo 'Это было выполнено'
+ v=
+ [[ -z '' ]]
+ echo '$v is empty'

Шаг 4. Используйте расширенный режим отладки

Использование отладчика часто необходимо для понимания ошибок времени выполнения или логики в программе, это также может быть верно для большого сценария оболочки.

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

 

Оболочка Bash может запускаться в расширенном режиме отладки с помощью опции shell extdebug, которая позволяет интерактивному отладчику Bash шаг за шагом исследовать и отлаживать ваш скрипт.

При использовании shopt -s extdebug оболочка также автоматически включит set встроенную опцию errtrace и functrace для отслеживания ошибок в функциях bash. С помощью опций errtrace и functrace вы получаете доступ к DEBUG trap, RETURN ловушке и специальным переменным BASH_ARGC Bash, BASH_ARGV и BASH_ARGV0.

⚠️ Опция extdebug зависит от утилиты bashdb. Если она не установлена, вы получите сообщение об ошибкеwarning: cannot start debugger; debugging mode disabled. Команда bashdb может быть недоступна на вашей платформе и может потребовать ручной установки. В Ubuntu 20.04 вы можете использовать этот bashdb PPA, а на Mac вы можете использовать brew install bashdb. Если вы используете VS codium или VS code, вы можете использовать расширение vscode-bash-debug, которое включает в себя bashdb.

 

В дополнение к extdebug опции вы можете вызвать отладчик bash непосредственно из командной строки, используя одну из приведенных ниже команд:

bashdb [options] [--] script-name [script options]
bashdb [options] -c execution-string
bash --debugger [bash-options...] script-name [script options]

👉 Если вы не хотите или не можете использовать отладчик bashdb, вы можете заменить использование shopt -s extdebug параметров отладки/трассировки оболочки на set -o errtrace и set -o functrace, которые предоставляют большую часть функциональности, описанной ниже, за исключением интерактивного пошагового отладчика.

 

Чтобы управлять режимом отладчика в нашем текущем примере, вы можете добавить небольшой блок my-debug-env в сценарий запуска, который установит необходимые параметры оболочки в зависимости от того, будет установлена пользовательская переменная среды DEBUGGER или нет.

if [[ -v DEBUGGER ]]; then
  shopt -s extdebug
else
  set -o errtrace
  set -o functrace
fi

 

Важно, чтобы опции errtrace и functrace были включены тем или иным способом, поскольку это гарантирует, что ловушки ERRDEBUG RETURN будут унаследованы заменой команд, функциями оболочки и подоболочками.

[andreyex@linux ~]$ trap 'echo "ERR trap from ${FUNCNAME:-MAIN} context."' ERR
[andreyex@linux ~]$ false
ERR trap from MAIN context.
[andreyex@linux ~]$ fn() { false; }; fn ; echo "fn exit code is $?"
ERR trap from MAIN context.
fn exit code is 1
[andreyex@linux ~]$ fn() { false; true; }; fn ; echo "fn exit code is $?"
fn exit code is 0
[andreyex@linux ~]$ set -o errtrace
[andreyex@linux ~]$ fn() { false; true; }; fn ; echo "fn exit code is $?"
ERR trap from fn context.
fn exit code is 0

 

Наиболее распространенной командой внутри отладчика Bash, bashdb, будет set linetrace on печатать каждую выполненную команду, print var чтобы отобразить текущее присвоенное значение переменной varstep n (или s n) перейти к следующему действию за n шагов (n = 1, если не указано) и cont (или c) продолжить выполнение полного сценария.

[andreyex@linux ~]$ DEBUGGER=1 BASH_ENV=my-debug-env ./example-debug param1
bash debugger, bashdb, release 5.0-1.1.2

Copyright 2002-2004, 2006-2012, 2014, 2016-2019 Rocky Bernstein
This is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.

(/home/jdoe/example-debug:3):
3:      echo $BASH_ENV
bashdb<0> set linetrace on
bashdb<1> step
my-debug-env
(/home/jdoe/example-debug:4):
4:      set -o
bashdb<2> cont
(/home/jdoe/example-debug:16):
level 1, subshell 0, depth 0:   echo "Example Script..."
Example Script...
(/home/jdoe/example-debug:17):
level 1, subshell 0, depth 0:   
echo "Main BASH_ARGC=${BASH_ARGC[@]} BASH_ARGV=${BASH_ARGV[@]}"
Main BASH_ARGC=1 BASH_ARGV=param1
...

 

Переменные $BASH_ARGC и $BASH_ARGV представляют собой массивы, содержащие информацию о скрипте и позиционных параметрах функций. $BASH_ARGC содержит количество параметров в каждом фрейме текущего стека вызовов выполнения bash, упорядоченных с последнего по первый вызов, в то время как $BASH_ARGV содержит все параметры в текущем стеке вызовов выполнения bash, упорядоченные с последнего по первый вызов.

### Example Script
#!/usr/bin/env bash
shopt -s extdebug
debug() {
 echo "Func: ${BASH_ARGC[@]} ${BASH_ARGV[@]}"
}

echo "Main: ${BASH_ARGC[@]} ${BASH_ARGV[@]}"
debug c

### Example output
[andreyex@linux ~]$ ./example-argdebug a b
Main: 2 b a
Func: 1 2 c b a

 

$BASH_ARGV0 идентичен специальному параметру $0 и расширяется до имени текущей оболочки или сценария оболочки. Присвоение нового значения $BASH_ARGV0 также обновит $0 специальный параметр. $BASH_ARGV0 при отмене настройки он потеряет свои специальные свойства. Эта переменная доступна начиная с версии Bash 5.

[andreyex@linux ~]$ echo $0 $BASH_ARGV0
/usr/local/bin/bash /usr/local/bin/bash

 

Шаг 5: предоставьте значимые журналы отладки

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

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

[andreyex@linux ~]$ trap 'echo "$BASH_COMMAND" failed with error code $?' ERR
[andreyex@linux ~]$ bad_command &>/dev/null
bad_command &> /dev/null failed with error code 127

 

$BASH_LINENO и $BASH_SOURCE являются переменными массива bash. Эти переменные часто используются для отладки и ведения журнала и работают совместно с переменной функции bash$FUNCNAME. Переменная $BASH_SOURCE содержит исходные имена файлов, в которых определены соответствующие имена функций оболочки в $FUNCNAME переменной массива. Переменная $BASH_LINENO содержит номера строк в исходных файлах, где был вызван каждый соответствующий элемент $FUNCNAME.

$LINENO содержит номер строки в скрипте или функции оболочки, выполняемой в данный момент.

### пример script
### Filename: example-debug
#!/usr/bin/env bash
debug() {
echo "Func BASH_SOURCE: ${!BASH_SOURCE[@]} ${BASH_SOURCE[@]}"
echo "Func BASH_LINENO: ${!BASH_LINENO[@]} ${BASH_LINENO[@]}"
echo "Func FUNCNAME: ${!FUNCNAME[@]} ${FUNCNAME[@]}"
echo "Func LINENO: ${LINENO}"}echo "Example Script..."
echo "Main BASH_SOURCE: ${!BASH_SOURCE[@]} ${BASH_SOURCE[@]}"
echo "Main BASH_LINENO: ${!BASH_LINENO[@]} ${BASH_LINENO[@]}"
echo "Main FUNCNAME: ${!FUNCNAME[@]} ${FUNCNAME[@]}"
echo "Main LINENO: ${LINENO}"debug

### Example output[andreyex@linux ~]$ ./example-debug 
Example Script...
Main BASH_SOURCE: 0 ./example-debug
Main BASH_LINENO: 0 0Main FUNCNAME:
Main LINENO: 13
Func BASH_SOURCE: 0 1 ./example-debug ./example-debug
Func BASH_LINENO: 0 1 12 0
Func FUNCNAME: 0 1 debug main
Func LINENO: 7

 

Некоторые другие переменные оболочки, которые, возможно, стоит использовать, включают в себя BASHPID, PID, BASH, SHELL, BASHOPTS, SHELLOPTS, POSIXLY_CORRECT, BASH_COMPAT. Эти переменные может быть полезно напечатать в начале или конце вывода журнала, чтобы узнать, в какой текущей среде выполняется ваш скрипт. Часто вы можете предположить, что какая-то опция оболочки включена, или обнаружить, что вы работаете в режиме совместимости, который отключает некоторые функции, которые вы ожидаете иметь.

 

Полный пример

На основе этих подробных шагов мы можем создать простой скрипт инициализации среды отладки Bash, который будет использоваться всякий раз, когда мы захотим отладить или профилировать скрипт bash.

👉 Не стесняйтесь изменять приведенный ниже пример, добавляя необходимый уровень отладочной информации, чтобы он соответствовал вашим потребностям.

 

### Определите среду отладки
### Filename: my-debug-env

PS4='+[$0:$LINENO] '

if [[ -v DEBUGGER ]]; then
  shopt -s extdebug
else
  set -o errtrace
  set -o functrace
fi

if [[ -v TRACE ]]; then
  echo "Run TRACE mode"
  exec 4>./xtrace.out
  BASH_XTRACEFD=4
  set -o xtrace # same as set -x
fi

if [[ -v NOOP ]]; then
  echo "Run NOOP mode"
  set -o noexec # same as set -n
fi

debug() {
 echo "[ DEBUG ]| BASH_COMMAND=${BASH_COMMAND}"
 echo "         | BASH_ARGC=${BASH_ARGC[@]} BASH_ARGV=${BASH_ARGV[@]}"
 echo "         | BASH_SOURCE: ${!BASH_SOURCE[@]} ${BASH_SOURCE[@]}"
 echo "         | BASH_LINENO: ${!BASH_LINENO[@]} ${BASH_LINENO[@]}"
 echo "         | FUNCNAME: ${!FUNCNAME[@]} ${FUNCNAME[@]}"
}

trap 'echo ERR trap from ${FUNCNAME:-MAIN} context. $BASH_COMMAND failed with error code $?' ERR
trap 'debug' DEBUG

 

Мы можем попробовать это с помощью простого примера скрипта.

#!/usr/bin/env bash
# Filename: ./example-xtrace
echo "Это было выполнено"
v=$1
if [[ -z "${v}" ]]; then
echo "\$v is empty"
fi
## Debug Output
[andreyex@linux ~]$ TRACE=1 BASH_ENV=my-debug-env ./example-xtrace param1
Run TRACE mode
[ DEBUG ]| BASH_COMMAND=echo "Это было выполнено"
| BASH_ARGC=1 BASH_ARGV=param1
| BASH_SOURCE: 0 1 my-debug-env ./example-xtrace
| BASH_LINENO: 0 1 3 0
| FUNCNAME: 0 1 debug main
Это было выполнено
[ DEBUG ]| BASH_COMMAND=v=$1
| BASH_ARGC=1 BASH_ARGV=param1
| BASH_SOURCE: 0 1 my-debug-env ./example-xtrace
| BASH_LINENO: 0 1 4 0
| FUNCNAME: 0 1 debug main
[ DEBUG ]| BASH_COMMAND=[[ -z "${v}" ]]
| BASH_ARGC=1 BASH_ARGV=param1
| BASH_SOURCE: 0 1 my-debug-env ./example-xtrace
| BASH_LINENO: 0 1 5 0
| FUNCNAME: 0 1 debug main

# XTRACE Logs[andreyex@linux ~]$ cat xtrace.out 
+[bash:20] [[ -v NOOP ]]
+[bash:33] trap 'echo ERR trap from ${FUNCNAME:-MAIN} context. $BASH_COMMAND failed with error code $?' ERR
+[bash:34] trap debug DEBUG
++[./example-xtrace:3] debug
++[./example-xtrace:26] echo '[ DEBUG ]| BASH_COMMAND=echo "Это было выполнено"'
++[./example-xtrace:27] echo '         | BASH_ARGC=1 BASH_ARGV=param1'
++[./example-xtrace:28] echo '         | BASH_SOURCE: 0' '1 my-debug-env' ./example-xtrace
++[./example-xtrace:29] echo '         | BASH_LINENO: 0' '1 3' 0
++[./example-xtrace:30] echo '         | FUNCNAME: 0' '1 debug' main
+[./example-xtrace:3] echo 'Это было выполнено'
++[./example-xtrace:4] debug
++[./example-xtrace:26] echo '[ DEBUG ]| BASH_COMMAND=v=$1'
++[./example-xtrace:27] echo '         | BASH_ARGC=1 BASH_ARGV=param1'
++[./example-xtrace:28] echo '         | BASH_SOURCE: 0' '1 my-debug-env' ./example-xtrace
++[./example-xtrace:29] echo '         | BASH_LINENO: 0' '1 4' 0
++[./example-xtrace:30] echo '         | FUNCNAME: 0' '1 debug' main
+[./example-xtrace:4] v=param1
++[./example-xtrace:5] debug
++[./example-xtrace:26] echo '[ DEBUG ]| BASH_COMMAND=[[ -z "${v}" ]]'
++[./example-xtrace:27] echo '         | BASH_ARGC=1 BASH_ARGV=param1'
++[./example-xtrace:28] echo '         | BASH_SOURCE: 0' '1 my-debug-env' ./example-xtrace
++[./example-xtrace:29] echo '         | BASH_LINENO: 0' '1 5' 0
++[./example-xtrace:30] echo '         | FUNCNAME: 0' '1 debug' main
+[./example-xtrace:5] [[ -z param1 ]]

 

Используя этот подход, хотя он и является маловероятным вариантом использования, вы также можете отлаживать команды Bash из командной строки, используя опцию -c.

[andreyex@linux ~]$ y=9
[andreyex@linux ~]$ TRACE=1 BASH_ENV=my-debug-env bash -c "x=1; y=$y; echo \$((x+y))"
Run TRACE mode
[ DEBUG ]| BASH_COMMAND=x=1
| BASH_ARGC=0 BASH_ARGV=
| BASH_SOURCE: 0 my-debug-env
| BASH_LINENO: 0 0
| FUNCNAME: 0 debug
[ DEBUG ]| BASH_COMMAND=y=9
| BASH_ARGC=0 BASH_ARGV=
| BASH_SOURCE: 0 my-debug-env
| BASH_LINENO: 0 0| FUNCNAME: 0 debug
[ DEBUG ]| BASH_COMMAND=echo $((x+y))| BASH_ARGC=0 BASH_ARGV=
| BASH_SOURCE: 0 my-debug-env
| BASH_LINENO: 0 0
| FUNCNAME: 0 debug
10[andreyex@linux ~]$ cat xtrace.out 
+[bash:20] [[ -v NOOP ]]
+[bash:33] trap 'echo ERR trap from ${FUNCNAME:-MAIN} context. $BASH_COMMAND failed with error code $?' ERR
+[bash:34] trap debug DEBUG
++[bash:0] debug
++[bash:26] echo '[ DEBUG ]| BASH_COMMAND=x=1'
++[bash:27] echo '         | BASH_ARGC=0 BASH_ARGV='
++[bash:28] echo '         | BASH_SOURCE: 0 my-debug-env'
++[bash:29] echo '         | BASH_LINENO: 0 0'
++[bash:30] echo '         | FUNCNAME: 0 debug'+[bash:0] x=1++[bash:0] debug
++[bash:26] echo '[ DEBUG ]| BASH_COMMAND=y=9'
++[bash:27] echo '         | BASH_ARGC=0 BASH_ARGV='
++[bash:28] echo '         | BASH_SOURCE: 0 my-debug-env'
++[bash:29] echo '         | BASH_LINENO: 0 0'
++[bash:30] echo '         | FUNCNAME: 0 debug'+[bash:0] y=9++[bash:0] debug
++[bash:26] echo '[ DEBUG ]| BASH_COMMAND=echo $((x+y))'
++[bash:27] echo '         | BASH_ARGC=0 BASH_ARGV='
++[bash:28] echo '         | BASH_SOURCE: 0 my-debug-env'
++[bash:29] echo '         | BASH_LINENO: 0 0'
++[bash:30] echo '         | FUNCNAME: 0 debug'
Exit mobile version