Иногда скрипты используют коды выхода, а иногда нет. Кажется, что коды выхода легко забываются, но они являются невероятно важной частью любого скрипта. Особенно если этот сценарий используется для командной строки.
Что такое коды выхода?
В системах Unix и Linux программы при завершении могут передавать значение своему родительскому процессу. Это значение называется кодом выхода или статусом выхода. В системах POSIX стандартным соглашением является то, что программа передает 0 для успешного выполнения и 1 или выше для неудачного выполнения.
Почему это важно? Если рассматривать коды выхода в контексте скриптов, написанных для использования в командной строке, то ответ очень прост. Любой полезный скрипт неизбежно будет либо использован в другом скрипте, либо обернут в bash one liner. Это становится особенно актуальным, если скрипт используется с инструментами автоматизации, такими как SaltStack, или инструментами мониторинга, такими как Nagios. Эти программы выполняют скрипты и проверяют код состояния, чтобы определить, был ли этот скрипт успешным или нет.
Кроме того, коды выхода существуют в ваших сценариях, даже если вы их не определяете. Не определив должным образом коды выхода, вы можете ложно сообщать об успешном выполнении, что может вызвать проблемы в зависимости от того, что делает сценарий.
Что произойдет, если я не укажу код выхода
В Linux любой скрипт, запущенный из командной строки, имеет код выхода. В сценариях Bash, если код выхода не указан в самом сценарии, используется код выхода последней выполненной команды. Чтобы немного лучше объяснить коды выхода, мы воспользуемся примером сценария.
Образец сценария:
1 2 3 | #!/bin/bash touch /root/test echo "созданный файл" |
Приведенный выше пример сценария выполнит как команду touch, так и команду echo. Когда мы выполним этот сценарий (в качестве пользователя, не являющегося пользователем root), команда touch завершится неудачей, в идеале, поскольку команда touch завершилась неудачей, мы хотели бы, чтобы код выхода сценария указывал на неудачу с соответствующим кодом выхода. Чтобы проверить код выхода, мы можем просто вывести специальную переменную $? в bash. Эта переменная выведет код выхода последней запущенной команды.
Выполнение:
1 2 3 4 5 | ./tmp.sh touch: cannot touch '/root/test': Permission denied созданный файл echo $? 0 |
Как вы можете видеть, после выполнения команды ./tmp.sh код выхода был равен 0, что означает успех, хотя команда touch завершилась неудачей. В примере сценария выполняются две команды touch и echo, поскольку мы не указали код выхода, сценарий завершается с кодом выхода последней запущенной команды. В данном случае последней запущенной командой является команда echo, которая выполнилась успешно.
Скрипт:
1 2 | #!/bin/bash touch /root/test |
Если мы удалим команду echo из сценария, то увидим код выхода команды touch.
Выполнение:
1 2 3 4 | ./tmp.sh touch: cannot touch '/root/test': Permission denied echo $? 1 |
Как вы можете видеть, поскольку последней выполненной командой была touch, код выхода отражает истинное состояние сценария; не удалось.
Использование кодов выхода в сценариях bash
Хотя удаление команды echo из нашего примера сценария сработало для обеспечения кода выхода, что происходит, когда мы хотим выполнить одно действие, если касание было успешным, и другое, если оно не было успешным. Например, печать на stdout при успехе и на stderr при неудаче.
Тестирование кодов выхода
Ранее мы использовали специальную переменную $? для печати кода выхода из сценария. Мы также можем использовать эту переменную внутри нашего сценария, чтобы проверить, была ли команда touch успешной или нет.
1 2 3 4 5 6 7 8 9 10 | #!/bin/bash touch /root/test 2> /dev/null if [ $? -eq 0 ] then echo "Успешно создан файл" else echo "Не удалось создать файл" >&2 fi |
В приведенной выше редакции нашего примера сценария; если код выхода для touch равен 0, сценарий выдаст сообщение об успехе. Если код выхода не равен 0, это означает неудачу, и сценарий выдаст сообщение о неудаче в stderr.
Выполнение:
1 2 | ./tmp.sh Не удалось создать файл |
Предоставление собственного кода выхода
Хотя приведенная выше ревизия выдаст сообщение об ошибке, если команда touch не выполнится, она все равно предоставляет код выхода 0, указывающий на успех.
1 2 3 4 | ./tmp.sh Не удалось создать файл echo $? 0 |
Поскольку сценарий не удался, было бы не очень хорошо передавать успешный код выхода любой другой программе, выполняющей этот сценарий. Чтобы добавить собственный код выхода к этому сценарию, мы можем просто использовать команду exit.
Скрипт:
1 2 3 4 5 6 7 8 9 10 11 12 | #!/bin/bash touch /root/test 2> /dev/null if [ $? -eq 0 ] then echo "Успешно создан файл" exit 0 else echo "Не удалось создать файл" >&2 exit 1 fi |
С помощью команды exit в этом сценарии мы выйдем с успешным сообщением и кодом выхода 0, если команда touch выполнена успешно. Если же команда touch не выполнится, мы выведем сообщение о неудаче на stderr и выйдем с кодом выхода 1, что означает неудачу.
Выполнение:
1 2 3 4 5 | ./tmp.sh Не удалось создать файл echo $? 1 |
Использование кодов выхода в командной строке
Теперь, когда наш скрипт способен сообщить пользователям и программам об успешном или неуспешном завершении, мы можем использовать этот скрипт с другими инструментами администрирования или просто использовать его в bash.
1 2 3 4 5 | # ./tmp.sh && echo "бам" || (sudo ./tmp.sh && echo "бам" || echo "ошибка") Не удалось создать файл Успешно создан файл бам |
Приведенная выше группа команд использует то, что в bash называется конструкцией списка. Конструкции списка позволяют вам объединять команды в цепочки с помощью простых условий && для and и || для or. Приведенная выше команда выполнит сценарий ./tmp.sh, и если код выхода равен 0, то будет выполнена команда echo "бам". Однако если код выхода из ./tmp.sh равен 1, то дальше будут выполняться команды, заключенные в круглые скобки. Внутри скобки команды соединяются вместе с помощью конструкций && и ||.
Конструкции списка используют коды выхода, чтобы понять, успешно выполнилась команда или нет. Если скрипты не используют коды выхода должным образом, любой пользователь этих скриптов, использующий более продвинутые команды, такие как конструкции списков, получит неожиданные результаты при сбоях.
Другие коды выхода
Команда exit в bash принимает целые числа от 0 до 255, в большинстве случаев достаточно 0 и 1, однако есть и другие зарезервированные коды выхода, которые могут быть использованы для более специфических ошибок.