Bash ігнорує помилку для певної команди


444

Я використовую наступні варіанти

set -o pipefail
set -e

У скрипті bash для зупинки виконання помилки. У мене виконано ~ 100 рядків сценарію, і я не хочу перевіряти код повернення кожного рядка в сценарії.

Але для однієї конкретної команди я хочу проігнорувати помилку. Як я можу це зробити?

Відповіді:


757

Рішення:

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. Як пояснює Довідковий посібник Баша , "оболонка не виходить", коли -eатрибут встановлений ", якщо команда, яка не виходить, є частиною списку команд відразу після whileабо untilключового слова, частина тесту в ifоператорі, частина будь-якої команди, виконаної в a &&або ||списку, за винятком команди, що слідує за остаточним, &&або ||будь-яка команда в конвеєрі, але остання, або якщо стан повернення команди інвертується !. "
ruakh

@IgorChubin Я не знаю, чому, але це не працює вихід = (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

87

Не зупиняйтеся, а також зберігайте статус виходу

На всякий випадок, якщо ви хочете, щоб ваш скрипт не зупинявся, якщо певна команда не спрацьовує, і ви також хочете зберегти код помилки невдалої команди:

set -e
EXIT_CODE=0
command || EXIT_CODE=$?
echo $EXIT_CODE

2
Це саме те, що мені було потрібно
sarink

чомусь не працює для мене ... ой добре
Jeef

EXIT_CODE не встановлено на нуль, коли команда повертається 0. Хоча не нульові коди виходу захоплюються. Будь-яка ідея чому?
Ankita13

@ Ankita13 Тому що якщо це було нульове значення, перший commandбув успішним, і потрібно запустити те, що після ||. читайте це як: якщо commandне вдалося, зробіть EXIT_CODE=$?. Ви, ймовірно, могли просто зробити command || echo "$?"або використати "пастку" для більш детальної налагодження. Дивіться це -> stackoverflow.com/a/6110446/10737630
tinnick

63

Більш коротко:

! particular_script

З специфікації POSIX щодо set -e(міна акценту):

Якщо цей параметр увімкнено, якщо проста команда не спрацьовує з будь-якої з причин, перелічених у Наслідках помилок оболонки, або повертає значення статусу виходу> 0, і не є частиною складеного списку через деякий час, до, або, якщо ключове слово, і не є частиною списку І або АБО, і не є конвеєром, якому передує! Зарезервоване слово , тоді оболонка негайно вийде.


16
Це просто інвертує вихідний код команди, тому команда, що закінчилася успішно, поверне 1 замість 0 та не виконає сценарій -e.
Марбоні

17
Я розумію, що !воля не дозволить оболонці вийти незалежно від того, що. Цей скрипт відображає повідомлення, Still alive!коли я його запускаю, вказуючи на те, що сценарій закінчився. Ви бачите різну поведінку?
Лілі Фінлі

7
Ви маєте рацію, він перевертає статус виходу, але не припиняє сценарій, коли команда закінчується обома 0 або 1. Я був неуважним. У будь-якому випадку, дякую, що ви цитували документацію, я висловив свою ifфразу і вирішив проблему.
Марбоні

42

Замість "повернення істини" ви також можете скористатися утилітою "noop" або null (як зазначено в специфікаціях POSIX ) :і просто "нічого не робити". Ви збережете кілька листів. :)

#!/usr/bin/env bash
set -e
man nonexistentghing || :
echo "It's ok.."

3
хоча ||: не так зрозуміло, як || правда (що може бентежити користувачів, які не знають експертів), мені подобається стислість
Девід,

Цей варіант не повертає жодного тексту, який може бути важливим для CI.
ківаґант

5

Якщо ви хочете попередити збій сценарію та зібрати код повернення:

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

set -e

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

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


2

Я використовую фрагмент нижче, працюючи з інструментами CLI, і хочу знати, чи існує якийсь ресурс чи ні, але мене не хвилює вихід.

if [ -z "$(cat no_exist 2>&1 >/dev/null)" ]; then
    echo "none exist actually exist!"
fi

1
Здається, це справді кругленький і помірно дорогий спосіб сказатиif [ -r not_exist ]
tripleee

1

Мені подобається таке рішення:

: `particular_script`

Команда / скрипт між зворотними кліщами виконується, а його вихід подається команді ":" (що еквівалентно "true")

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

редагувати: виправлено потворні помилки



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. Таким чином друкуються повідомлення про помилки, але весь сценарій продовжує виконуватись.


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

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якщо цього потрібно.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.