Linux обработка ошибок bash

Обновлено: 04.07.2024

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

есть ли команда try CATCH в Bash?

Bash не имеет столько роскоши, сколько можно найти во многих языках программирования.

нет try/catch в bash; однако можно добиться аналогичного поведения, используя && или || .

если command1 Не то command2 работает следующим образом

аналогично, используя && , command2 будет выполняться, если command1 успешно

самое близкое от try/catch следующим образом

также bash содержит некоторые механизмы обработки ошибок, а также

он немедленно остановит ваш скрипт, если простая команда не удастся. Я думаю, это должно было быть поведение по умолчанию. Поскольку такие ошибки почти всегда означают что-то неожиданное, не совсем "разумно" продолжать выполнять следующие команды.

а также почему бы и нет if. else . Это твой лучший друг.

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

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

Я разработал почти безупречную реализацию try & catch в bash, которая позволяет писать код, например:

вы даже можете вложить блоки try-catch внутри себя!

код является частью моего bash boilerplate / framework. Это еще больше расширяет идею try & catch с такими вещами, как обработка ошибок с помощью backtrace и исключений (плюс некоторые другие приятные функции).

вот код, который отвечает только на try & catch:

не стесняйтесь использовать, вилка и внести свой вклад-это на GitHub.

bash не прерывает выполнение в случае, если sth обнаруживает состояние ошибки (если вы не установили -e флаг). Языки программирования, которые предлагают try/catch сделайте это для того, чтобы тормозят a "спасение" из-за этой особой ситуации (поэтому обычно называется "исключением").

на bash , вместо этого, только команда, о которой идет речь, выйдет с кодом выхода больше 0, указывая на это состояние ошибки. Вы можете проверить это конечно, но так нет автоматического спасение ничего, a попробовать/поймать не имеет смысла. Ему просто не хватает этого контекста.

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

вместо some_condition С if вы также можете просто выполнить команду, и в случае не (имеет код выхода больше 0), залог out:

к сожалению, используя этот метод, вы ограничены 255 различными кодами выхода (1..255) и никакие приличные объекты исключений не могут использоваться.

Если вам нужна дополнительная информация для передачи вместе с моделируемым исключением, вы можете использовать stdout подрешеток, но это немного сложно и, возможно, другой вопрос; -)

используя вышеуказанные -e флаг в оболочке вы можете даже лишить этого явного exit заявление:

как все говорят, bash не имеет правильного синтаксиса try/catch, поддерживаемого языком. Вы можете запустить bash с помощью -e аргумент или использовать set -e внутри скрипта, чтобы прервать весь процесс bash, если любая команда имеет ненулевой код выхода. (Вы также можете set +e для временного разрешения сбоев команд.)

Итак, один из методов моделирования блока try / catch-запустить подпроцесс для выполнения работы с -e включено. Затем в основном процессе проверьте код возврата подпроцесс.

Bash поддерживает строки heredoc, поэтому вам не нужно писать два отдельных файла для обработки этого. В приведенном ниже примере TRY heredoc будет работать в отдельном экземпляре bash с -e включено, поэтому подпроцесс аварийно завершит работу, если какая-либо команда вернет ненулевой код выхода. Затем, вернувшись в основной процесс, мы можем проверить код возврата для обработки блока catch.

это не правильный поддерживаемый языком блок try/catch, но он может поцарапать аналогичный зуд для тебя.

Как использовать коды завершения в Bash-скриптах главное изображение

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

Что такое коды завершения

В Linux и других Unix-подобных операционных системах программы во время завершения могут передавать значение родительскому процессу. Это значение называется кодом завершения или состоянием завершения. В POSIX по соглашению действует стандарт: программа передаёт 0 при успешном исполнении и 1 или большее число при неудачном исполнении.

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

Что происходит, когда коды завершения не определены

В Linux любой код, запущенный в командной строке, имеет код завершения. Если код завершения не определён, Bash-скрипты используют код выхода последней запущенной команды. Чтобы лучше понять суть, обратите внимание на пример.

Этот скрипт запускает команды touch и echo . Если запустить этот скрипт без прав суперпользователя, команда touch не выполнится. В этот момент мы хотели бы получить информацию об ошибке с помощью соответствующего кода завершения. Чтобы проверить код выхода, достаточно ввести в командную строку специальную переменную $? . Она печатает код возврата последней запущенной команды.

Как видно, после запуска команды ./tmp.sh получаем код завершения 0 . Этот код говорит об успешном выполнении команды, хотя на самом деле команда не выполнилась. Скрипт из примера выше исполняет две команды: touch и echo . Поскольку код завершения не определён, получаем код выхода последней запущенной команды. Это команда echo , которая успешно выполнилась.

Если убрать из скрипта команду echo , можно получить код завершения команды touch .

Поскольку touch в данном случае — последняя запущенная команда, и она не выполнилась, получаем код возврата 1 .

Как использовать коды завершения в Bash-скриптах

Удаление из скрипта команды echo позволило нам получить код завершения. Что делать, если нужно сделать разные действия в случае успешного и неуспешного выполнения команды touch ? Речь идёт о печати stdout в случае успеха и stderr в случае неуспеха.

Проверяем коды завершения

Выше мы пользовались специальной переменной $? , чтобы получить код завершения скрипта. Также с помощью этой переменной можно проверить, выполнилась ли команда touch успешно.

После рефакторинга скрипта получаем такое поведение:

  • Если команда touch выполняется с кодом 0 , скрипт с помощью echo сообщает об успешно созданном файле.
  • Если команда touch выполняется с другим кодом, скрипт сообщает, что не смог создать файл.

Создаём собственный код завершения

Наш скрипт уже сообщает об ошибке, если команда touch выполняется с ошибкой. Но в случае успешного выполнения команды мы всё также получаем код 0 .

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

Как использовать коды завершения в командной строке

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

В примере выше && используется для обозначения «и», а || для обозначения «или». В данном случае команда выполняет скрипт ./tmp.sh , а затем выполняет echo "bam" , если код завершения 0 . Если код завершения 1 , выполняется следующая команда в круглых скобках. Как видно, в скобках для группировки команд снова используются && и || .

Скрипт использует коды завершения, чтобы понять, была ли команда успешно выполнена. Если коды завершения используются некорректно, пользователь скрипта может получить неожиданные результаты при неудачном выполнении команды.

Дополнительные коды завершения

Команда exit принимает числа от 0 до 255 . В большинстве случаев можно обойтись кодами 0 и 1 . Однако есть зарезервированные коды, которые обозначают конкретные ошибки. Список зарезервированных кодов можно посмотреть в документации.

Адаптированный перевод статьи Understanding Exit Codes and how to use them in bash scripts by Benjamin Cane. Мнение администрации Хекслета может не совпадать с мнением автора оригинальной публикации.

xotkot

скрипт завершается при ошибке в выполненной команде только если bash был запущен с ключом -e.
Можно отменить эффект этого ключа выполнив команду set -e
Или добавить к команде, которая может завершиться с ошибкой || : например так:
do-something || :
Или завернуть вызов такой команды в условие, например так:


Более подробно можно прочитать в man 1 bash, в разделе о встроенной команде set.

Rou1997

Здравствуйте.
Пытаюсь на Android отлаживать нативные (NDK) приложения при помощи gdb.
Использую Genymotion, стало быть права su. Приложение запустил. gdb-server запустил. gdb запустил, вызываю target remote. Связь есть, команда continue работает (приложение сразу перестает "не отвечать").
Но никак не могу собственно загрузить библиотеку, чтобы был доступен список функций (info functions) и чтобы можно было ставить точки останова.
Не подскажете, как вообще загрузить ее на Android?
На Windows я это делал с помощью symbol-file remote:test.exe (тоже не без труда нашел), а для Android эта команда не поддерживается, вот не могу найти вообще.
Во всех туториалах по-разному и ничего не работает.
Пробовал так:
gdb D:\путь-на-компе\libTest.so
При этом библиотека вроде загружается, но она загружается не из памяти телефона, а собственно с компа. Поэтому info functions возвращает адреса не в памяти процесса приложения, а в файле .so.

Rou1997

jcmvbkbc: *читаю тэги* С Линуксом связан, с bash связан, и может даже с Ubuntu связан, пока я на Windows, но не пришлось бы на Ubuntu пересаживаться из-за того же NDK. А вы что хотите и зачем? :)

> Пытаюсь на Android отлаживать нативные (NDK) приложения при помощи gdb.
Rou1997: ваш вопрос про gdb. Вы задали его мне одному, как комментарий к ответу на совсем другой вопрос. Вы можете задать его нормально, и, я уверен, вам кто-нибудь ответит, может быть и я.

> А вы что хотите и зачем?
Я хочу порядка в примыкающей ко мне части вселенной. Мне так легче жить.

Он предлагает использовать следующую функцию для обработки ошибок в Bash:

У вас есть лучшая процедура обработки ошибок, которую вы используете в скриптах Bash?

. затем, когда вы создаете временный файл:

и $temp_foo будет удален при выходе, и будет напечатан текущий номер строки. ( set -e также даст вам поведение «выход при ошибке», хотя оно сопряжено с серьезными оговорками и ослабляет предсказуемость и переносимость кода).

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

Это прекрасное решение. Я просто хотел добавить

как элементарный механизм ошибок. Он немедленно остановит ваш скрипт, если простая команда не удастся. Я думаю, что это должно было быть поведением по умолчанию: так как такие ошибки почти всегда означают что-то неожиданное, не совсем «нормально» продолжать выполнять следующие команды.

@CharlesDuffy, некоторые из ошибок можно преодолеть с помощью set -o pipefail @CharlesDuffy Спасибо, что указали на ошибки; в целом, я все еще думаю, что set -e имеет высокое соотношение выгод и затрат.

Итак, вот мой совет :

содержимое файла: lib.trap.sh

Пример использования:
содержимое файла: trap-test.sh

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

это здорово . вам нужно создать для него проект github, чтобы люди могли легко вносить улучшения и вносить свой вклад. Я объединил его с log4bash, и вместе он создает мощную среду для создания хороших скриптов bash. @ Лука - это действительно здорово! Ваша картина вдохновила меня на создание собственной реализации этого, которая продвинулась еще дальше. Я разместил это в своем ответе ниже . Брависсимо !! Это отличный способ отладки скрипта. Grazie mille Единственное, что я добавил, это проверка на OS X, подобная этой: case "$(uname)" in Darwin ) stderr_log="$stderr.log";; Linux ) stderr_log="/dev/shm/stderr.log";; * ) stderr_log="/dev/shm/stderr.log" ;; esac

Эквивалентная альтернатива "set -e"

Это делает значение флага несколько понятнее, чем просто "-e".

Случайное добавление: чтобы временно отключить флаг и вернуться к значению по умолчанию (для продолжения выполнения независимо от кодов выхода), просто используйте

Это исключает правильную обработку ошибок, упомянутую в других ответах, но быстро и эффективно (как bash).

использование $(foo) на голой линии, а не просто foo обычно неправильно. Зачем продвигать это, приводя в качестве примера?

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

Просто получая библиотеку, вы получаете из коробки следующее (то есть она остановит выполнение при любой ошибке, как если бы она использовалась set -e благодаря trap on ERR и некоторому bash-fu ):

Обработка ошибок в bash-oo-framework

Есть некоторые дополнительные функции, которые помогают обрабатывать ошибки, такие как try and catch или throw ключевое слово, которые позволяют вам прервать выполнение в точке, чтобы увидеть обратную трассировку. Кроме того, если терминал поддерживает его, он выплевывает смайлики Powerline, окрашивает части вывода для большой читабельности и подчеркивает метод, который вызвал исключение в контексте строки кода.

Недостатком является то, что он не переносим - код работает на bash, вероятно,> = 4 (но я думаю, что он может быть перенесен с некоторым усилием на bash 3).

Код разделен на несколько файлов для лучшей обработки, но меня вдохновила идея обратного следа из приведенного выше ответа Луки Боррионе .

Чтобы узнать больше или взглянуть на источник, смотрите GitHub:

Это внутри проекта Bash Object Oriented Framework . . К счастью, он имеет только 7.4k LOC (согласно GLOC ). ООП - Объектно-ориентированная боль? @ingyhere очень модульный (и дружественный к удалению), поэтому вы можете использовать только часть исключений, если это то, для чего вы пришли;)

Я предпочитаю что-то действительно легко назвать. Поэтому я использую то, что выглядит немного сложным, но простым в использовании. Я обычно просто копирую и вставляю приведенный ниже код в мои скрипты. Объяснение следует за кодом.

Я обычно помещаю вызов функции очистки в сторону функции error_exit, но это варьируется от сценария к сценарию, поэтому я не учел его. Ловушки улавливают общие сигналы завершения и следят за тем, чтобы все было очищено. Псевдоним - это то, что делает настоящую магию. Мне нравится проверять все на наличие ошибок. Так что в общем я называю программы "если!" Тип заявления. Вычитая 1 из номера строки, псевдоним скажет мне, где произошла ошибка. Это также очень просто, и в значительной степени идиотское доказательство. Ниже приведен пример (просто замените / bin / false на то, что вы собираетесь вызывать).

Можете ли вы расширить выражение «Мы должны явно разрешить псевдонимы» ? Я бы волновался, что это может привести к неожиданному поведению. Есть ли способ добиться того же с меньшим воздействием? Более короткий пример использования в bash и zsh false || die "hello death"

Еще одним соображением является код возврата для возврата. Просто " 1 " довольно стандартно, хотя есть несколько зарезервированных кодов выхода, которые использует сам bash , и на той же странице утверждается, что определяемые пользователем коды должны находиться в диапазоне 64-113, чтобы соответствовать стандартам C / C ++.

Вы могли бы также рассмотреть подход битового вектора, который mount использует для его кодов выхода:

OR - объединение кодов позволяет вашему скрипту сигнализировать о нескольких одновременных ошибках.

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

function Ключевое слово беспричинно POSIX-несовместимое. Подумайте о том, чтобы сделать свое заявление просто error() < , без function него. $ должно быть $? , или $ если вы настаиваете на использовании ненужных скобок; внутреннее $ неправильно. @CharlesDuffy к настоящему времени, POSIX безвозмездно несовместим с GNU / Linux (все же, я понимаю вашу точку зрения)

перед; я думаю, потому что «выход» для меня почему-то не удался. Вышеуказанные значения по умолчанию кажутся хорошей идеей.

Используя это, я смог создать действительно надежный bash-файл для некоторого автоматизированного процесса, и он остановится в случае ошибок и log.sh сообщит мне ( сделает это)

Попробуйте использовать синтаксис POSIX для определения функций - без function ключевых слов, просто error_exit() < . есть ли причина, почему вы не просто делаете cd /home/myuser/afolder || error_exit "Unable to switch to folder" ? @ Pierre-OlivierVares Нет особых причин не использовать ||. Это была просто выдержка из существующего кода, и я просто добавил строки «обработки ошибок» после каждой соответствующей строки. Некоторые из них очень длинные, и было просто чище иметь их на отдельной (немедленной) линии

Этот трюк полезен для пропущенных команд или функций. Имя отсутствующей функции (или исполняемого файла) будет передано в $ _

Не $_ будет доступен в функции так же, как $? ? Я не уверен, что есть какая-либо причина использовать один в функции, но не другой.

Эта функция в последнее время довольно хорошо меня обслуживает:

Вы вызываете его, добавляя 0 или последнее возвращаемое значение к имени команды для запуска, так что вы можете объединять команды без необходимости проверять наличие ошибок. С этим, этот блок заявления:

Если какая-либо из команд не выполняется, код ошибки просто передается в конец блока. Я нахожу это полезным, когда вы не хотите, чтобы последующие команды выполнялись, если предыдущая не удалась, но вы также не хотите, чтобы скрипт сразу выходил (например, внутри цикла).

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

Но || оператор необходим для предотвращения возврата из внешней функции перед очисткой. Хитрость заключается в том, чтобы запустить внутреннюю команду в фоновом режиме, а затем сразу же дождаться ее. wait Встроенный возвращают код завершения внутренней команды, и теперь вы используете || после того wait , а не внутренней функции, поэтому set -e работает должным образом внутри последний:

Вот общая функция, основанная на этой идее. Он должен работать во всех POSIX-совместимых оболочках, если вы удаляете local ключевые слова, т.е. заменяете все local x=y просто x=y :

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

Читайте также: