Ошибка игнорирования Bash для конкретной команды

446

Я использую следующие варианты

set -o pipefail
set -e

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

Но для одной конкретной команды я хочу игнорировать ошибку. Как я могу это сделать?

Вивек Гоэль
источник

Ответы:

758

Решение:

particular_script || true

Пример:

$ cat /tmp/1.sh
particular_script()
{
    false
}

set -e

echo one
particular_script || true
echo two
particular_script
echo three

$ bash /tmp/1.sh
one
two

three никогда не будет напечатан.

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

$ set -o pipefail
$ false | true ; echo $?
1
$ set +o pipefail
$ false | true ; echo $?
0
Игорь Чубин
источник
15
+1. Как объясняется в Справочном руководстве по Bash , «оболочка не -eзавершает работу », когда атрибут установлен, «если команда, которая не выполняется, является частью списка команд, следующего сразу за ключевым словом whileили until, частью теста в ifвыражении, частью любой выполняемой команды в списке &&или, ||за исключением команды, следующей за последней &&или ||любой командой в конвейере, кроме последней, или если статус возврата команды инвертируется !. "
Руах
@IgorChubin Я не знаю почему, но это не работает output = (ldd $2/bin/* || true) | grep "not found" | wc -lскрипт завершается после этой строки, когда ldd возвращает ошибку
Vivek Goel
1
(ldd $2/bin/* || true) | grep "not found" | wc -l || true
Игорь Чубин
1
из-за set -o pipefail. когда grepничего не находит, он возвращает ненулевой код выхода, и для оболочки достаточно думать, что весь канал имеет ненулевой код выхода.
Игорь Чубин
3
Если вы хотите сохранить возвращаемое значение (без выхода), попробуйте mycommand && true. Это позволяет вам проверять код возврата на последующих этапах и обрабатывать его программно.
Эд Ост
179

Просто добавьте || trueпосле команды, где вы хотите игнорировать ошибку.

Ларс Котхофф
источник
11
Я думаю, что важно добавить это: этот метод позволит сохранить код ответа и сообщение об ошибке, тогда как "!" Метод, описанный ниже, изменит код ответа и, следовательно, не выдаст ошибку. Это очень важно при использовании set -eи пытается захватить сообщение об ошибке: напримерset -e; TEST=$(foo 2>&1 || true); echo $TEST
Spanky
88

Не останавливайтесь и сохраняйте статус выхода

На всякий случай, если вы хотите, чтобы ваш скрипт не останавливался, если какая-то команда не выполнена, и вы также хотите сохранить код ошибки неудачной команды:

set -e
EXIT_CODE=0
command || EXIT_CODE=$?
echo $EXIT_CODE
Арслан Кадир
источник
2
Это именно то, что мне было нужно
Саринк
у меня почему-то не работает ... ну да ладно
Jeef
EXIT_CODE не устанавливается в ноль, когда команда возвращает 0. Хотя ненулевые коды выхода фиксируются. Есть идеи почему?
Ankita13
@ Ankita13 Потому что, если он был нулевым, первое commandбыло успешным, и нужно запускать то, что после ||. прочитайте это как: если commandпотерпит неудачу, сделайте EXIT_CODE=$?. Вы, вероятно, могли бы просто сделать command || echo "$?"или использовать «trap» для более подробной отладки. Смотрите это -> stackoverflow.com/a/6110446/10737630
Тинник
63

Более кратко:

! particular_script

Из спецификации POSIX относительно set -e(выделено мое):

Когда эта опция включена, если простая команда завершается неудачно по какой-либо из причин, перечисленных в разделе Последствия ошибок оболочки, или возвращает значение состояния выхода> 0, и не является частью составного списка через некоторое время, до или, если ключевое слово, и не является частью списка AND или OR, и не является конвейером, которому предшествует! зарезервированное слово , то оболочка должна немедленно выйти.

Лили Финли
источник
16
Это просто инвертирует код завершения команды, поэтому успешно завершенная команда вернет 1 вместо 0 и завершит выполнение сценария -e.
Марбони
17
Насколько я понимаю, что !оболочка не сможет выйти, несмотря ни на что. Этот сценарий отображает сообщение, Still alive!когда я его запускаю, указывая, что сценарий завершился. Вы видите другое поведение?
Лили Финли
7
вы правы, он инвертирует состояние выхода, но не вылетает сценарий, когда команда заканчивается на 0 или 1. Я был невнимателен. В любом случае, спасибо за цитирование документации, я выразил свое выражение в ifпредложении и решил проблему.
Марбони
42

Вместо «возврата true» вы также можете использовать утилиту «noop» или null (как указано в спецификации POSIX ) :и просто «ничего не делать». Вы сохраните несколько писем. :)

#!/usr/bin/env bash
set -e
man nonexistentghing || :
echo "It's ok.."
Timo
источник
3
хотя ||: не так ясно, как || правда (что может сбить с толку неопытных пользователей), мне нравится краткость
Дэвид
Этот вариант не возвращает текст, который может быть важен для CI.
Кивагант
5

Если вы хотите предотвратить сбой вашего скрипта и собрать код возврата:

command () {
    return 1  # or 0 for success
}

set -e

command && returncode=$? || returncode=$?
echo $returncode

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

volingas
источник
2

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

if [ -z "$(cat no_exist 2>&1 >/dev/null)" ]; then
    echo "none exist actually exist!"
fi
Payman
источник
1
Это, кажется, действительно окольный и умеренно дорогой способ сказатьif [ -r not_exist ]
tripleee
1

Мне вроде как это решение:

: `particular_script`

Команда / скрипт между обратными галочками выполняется, и ее вывод подается на команду ":" (что эквивалентно "true")

$ false
$ echo $?
1
$ : `false`
$ echo $?
0

редактировать: исправлена ​​некрасивая опечатка

Q-жизнь
источник
0

хотя || trueпредпочтительнее, но вы также можете сделать

var=$(echo $(exit 1)) # it shouldn't fail
Фото Блыск
источник
0

Отсюда ни одно решение не сработало, поэтому я нашел другое:

set +e
find "./csharp/Platform.$REPOSITORY_NAME/obj" -type f -iname "*.cs" -delete
find "./csharp/Platform.$REPOSITORY_NAME.Tests/obj" -type f -iname "*.cs" -delete
set -e

Это полезно для CI & CD. Таким образом, сообщения об ошибках печатаются, но весь сценарий продолжает выполняться.

Konard
источник
0
output=$(*command* 2>&1) && exit_status=$? || exit_status=$?
echo $output
echo $exit_status

Пример использования этого для создания файла журнала

log_event(){
timestamp=$(date '+%D %T') #mm/dd/yy HH:MM:SS
echo -e "($timestamp) $event" >> "$log_file"
}

output=$(*command* 2>&1) && exit_status=$? || exit_status=$?

if [ "$exit_status" = 0 ]
    then
        event="$output"
        log_event
    else
        event="ERROR $output"
        log_event
fi
Robert.C
источник
0

Спасибо за простое решение здесь сверху:

<particular_script/command> || true

Следующая конструкция может использоваться для дополнительных действий / устранения неполадок шагов сценария и дополнительных параметров управления потоком:

if <particular_script/command>
then
   echo "<particular_script/command> is fine!"
else
   echo "<particular_script/command> failed!"
   #exit 1
fi

Мы можем затормозить дальнейшие действия и exit 1при необходимости.

Алмаз Гареев
источник