Как сделать что-то условно, если команда выполнена успешно или не выполнена

283

Как я могу сделать что-то подобное в Bash?

if "`command` returns any error";
then
    echo "Returned an error"
else
    echo "Proceed..."
fi
Михаил Мрозек
источник

Ответы:

359

Как сделать что-то условно, если команда выполнена успешно или не выполнена

Это именно то, что ifделает заявление Bash :

if command ; then
    echo "Command succeeded"
else
    echo "Command failed"
fi

Добавление информации из комментариев: вам не нужно использовать синтаксис [... ]в этом случае. [сама по себе команда, почти эквивалентна test. Это, вероятно, самая распространенная команда для использования в an if, что может привести к предположению, что она является частью синтаксиса оболочки. Но если вы хотите проверить, была ли команда выполнена успешно, используйте непосредственно саму команду if, как показано выше.

Изменить: процитировал вопрос в верхней части для ясности (этот ответ не появляется в верхней части страницы).

Кит Томпсон
источник
11
Обратите внимание, что точка с запятой важна.
Турбьёрн Равн Андерсен
21
Или вы можете просто поставить thenна отдельной строке.
10
36
@Joe: я думаю, что вы имеете в виду if ! command ; then ... ; fi. [сама по себе команда, и в этом случае она не нужна.
Кит Томпсон
20
@Joe: Мой путь также имеет достоинство быть правильным. if [ ! command ]не выполняется command; он обрабатывается commandкак строка и обрабатывается как истина, потому что он имеет ненулевую длину. [это синоним testкоманды
Кит Томпсон,
5
К сожалению. Ты прав.
Джо
133

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

rm -rf somedir && trace_output "Removed the directory"

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

rm -rf somedir || exit_on_error "Failed to remove the directory"

Или оба

rm -rf somedir && trace_output "Removed the directory" || exit_on_error "Failed to remove the directory"

Вероятно, неразумно делать очень много с этими конструкциями, но иногда они могут сделать процесс контроля намного понятнее.

Брюс Эдигер
источник
3
Они короче и (по крайней мере, в некоторых оболочках) быстрее. Я содрогнулся, вспомнив инсталляционный скрипт монстра Ultrix, написанный только с этими условными конструкциями, которые я однажды попытался расшифровать ...
vonbrand
101

Проверьте значение $?, которое содержит результат выполнения самой последней команды / функции:

#!/bin/bash

echo "this will work"
RESULT=$?
if [ $RESULT -eq 0 ]; then
  echo success
else
  echo failed
fi

if [ $RESULT == 0 ]; then
  echo success 2
else
  echo failed 2
fi

источник
11
Хотя это технически правильно (и, следовательно, не оправдывает понижения голосов), оно не использует ifидиому Баша . Я предпочитаю ответ Кита Томпсона.
Янмезен
16
У этой идиомы есть свои преимущества - она ​​сохраняет возвращаемое значение. В целом, я считаю, что это более мощный, хотя и более многословный. это также легче читать.
таксист
2
Что такое "Bash's if idiom"?
Новакер
3
@Nowaker Тот факт, что единственная цель ifсостоит в том, чтобы сделать это. Условия контроля потока в Баше все исследуют $?за кулисами; это то, что они делают. Явное изучение его значения должно быть ненужным в подавляющем большинстве случаев и, как правило, является антипаттерном для начинающих.
tripleee
1
@lunakid Если вы не заботитесь о коде выхода, if ! cmdэто нормально. В противном случае, общая договоренность заключается в использовании elseпункта. У вас не может быть пустого, thenно вы иногда видите, then : nothing; elseгде :неактивность важна. trueбудет работать там же.
tripleee
47

Это сработало для меня:

command && echo "OK" || echo "NOK"

если commandуспешно, то echo "OK"выполняется, и так как это успешно, выполнение останавливается там. В противном случае &&пропускается и echo "NOK"выполняется.

Немецкий ромм
источник
5
Если вы хотите что-то сделать в случае сбоя и сохранить код завершения (для отображения в командной строке или для проверки в скрипте), вы можете сделать это: command && echo "OK" || c=$?; echo "NOK"; $(exit $c)
Сэм Хаслер,
2
@ Сэм-Хаслер: разве этого не должно быть command && echo "OK" || (c=$?; echo "NOK"; (exit $c))?
jrw32982
9
Кроме того, если echo "OK"часть может сама выйти из строя, то это лучше:command && (echo "OK"; exit 0) || (c=$?; echo "NOK"; (exit $c))
jrw32982
@ jrw32982, Хорошо, я использовал первую конструкцию, но не последнюю.
Сэм Хаслер
Реальный ответ в комментариях, спасибо всем!
Джошуа Пинтер
6

Следует отметить, что if...then...fiи &&/ ||тип подхода имеет дело с состоянием выхода, возвращаемым командой, которую мы хотим проверить (0 в случае успеха); однако некоторые команды не возвращают ненулевой статус выхода, если команда не выполнена или не может обработать ввод. Это означает, что обычный ifи &&/ ||подходы не будут работать для этих конкретных команд.

Например, в Linux GNU по- fileпрежнему завершает работу с 0, если он получил несуществующий файл в качестве аргумента и findне может найти указанный файл пользователем.

$ find . -name "not_existing_file"                                          
$ echo $?
0
$ file ./not_existing_file                                                  
./not_existing_file: cannot open `./not_existing_file' (No such file or directory)
$ echo $?
0

В таких случаях одним из возможных способов справиться с ситуацией является чтение stderr/ stdinсообщения, например, те, которые были возвращены fileкомандой, или анализ выходных данных команды, как в find. Для этого caseможно использовать заявление.

$ file ./doesntexist  | while IFS= read -r output; do                                                                                                                  
> case "$output" in 
> *"No such file or directory"*) printf "%s\n" "This will show up if failed";;
> *) printf "%s\n" "This will show up if succeeded" ;;
> esac
> done
This will show up if failed

$ find . -name "doesn'texist" | if ! read IFS= out; then echo "File not found"; fi                                                                                     
File not found
Сергей Колодяжный
источник
2

Наиболее подверженной ошибкам я могла придумать:

  • Во-первых, получить значение. Предположим, вы делаете что-то вроде:

RR=$?

Теперь, не только для этой ситуации, но и для других, с которыми вы можете столкнуться, подумайте:

определенная переменная:

$ AA=1 ; if (( "10#0${AA}" == 1 )) ; then echo yes ; else echo no ; fi

Ответ: да

$ AA=1 ; if (( "10#0${AA}" != 1 )) ; then echo yes ; else echo no ; fi

Ответ: нет

Неопределенная переменная:

$ AA=1 ; if (( "10#0${BB}" == 1 )) ; then echo yes ; else echo no ; fi

Ответ: нет

$ AA=1 ; if (( "10#0${BB}" != 1 )) ; then echo yes ; else echo no ; fi

Ответ: да

$ AA=1 ; if (( "10#0${BB}" == 0 )) ; then echo yes ; else echo no ; fi

Ответ: да

Это предотвращает все виды ошибок.

Вы, наверное, знаете весь синтаксис, но вот несколько советов:

  • Используйте цитаты. Избегайте "blank"быть nothing.
  • Новое современное обозначение для переменных ${variable}.
  • Добавляя объединенный ноль перед вашим номером, также избегайте "вообще никакого номера".
  • Но подождите, добавление нуля заставит число стать base-8. Вы получите ошибку вроде:
    • value too great for base (error token is "08")для чисел выше 7. Вот когда 10#вступает в игру:
    • 10#заставляет число быть base-10.
Доктор Беко
источник
1

Ты можешь сделать это:

if ($( ping 4.4.4.4 -c1 > /dev/null )) ; then
  echo "ping response succsess!!!"
fi
Даниил
источник
9
Это работает, но запутанно. Вы запускаете ping в подоболочке подоболочки, вывод pingкоторой записывается с учетом запуска его в виде команды. Но поскольку выходные данные перенаправляются в / dev / null, это всегда будет пустой строкой. Таким образом, вы ничего не запускаете в подоболочке, что означает, что предыдущее состояние выхода (подзаголовок подстановки команд, то есть ping) будет сохранено. Очевидно, правильный путь if ping ...; thenздесь.
Стефан Шазелас
1

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

В этом примере ifпроверяется, существует ли файл и является ли он обычным файлом. Если эти условия выполняются, проверьте, имеет ли он размер больше 0.

#!/bin/bash

echo "Which error log are you checking today? "
read answer

if [ -f /opt/logs/$answer*.errors ]
    then
        if [ -s /opt/logs/$answer*.errors ]
            then
                echo "Content is present in the $answer error log file."
            else
                echo "No errors are present in the $answer error log file."
        fi
    else
        echo "$answer does not have an error log at this time."
fi
quartzinquartz
источник
2
Это то, что делает ваш ответ, но ваш ответ не касается вопроса.
Джефф Шаллер
@JeffSchaller, спасибо за вашу заметку. Я отредактировал свой пост, чтобы включить ссылку на вопрос.
кварцевый кварц
1
#!/bin/bash

if command-1 ; then
   echo "command-1 succeeded and now running from this block command-2"
   command-2
else
   echo "command-1 failed and now running from this block command-3"
   command-3
fi
Ник Владичяну
источник
3
Это похоже на уже принятый ответ ; пожалуйста, укажите, какую работу вы используете повторно, или добавьте свои собственные усилия в свои ответы.
Джефф Шаллер
-3

Это может быть сделано просто так, так как $?дает вам статус последней выполненной команды.

Так может быть

#!/bin/sh

... some command ...

if [ $? == 0 ] ; then
  echo '<the output message you want to display>'
else 
  echo '<failure message>'
fi
Випин Мишра
источник
1
Downvote: Это просто перефразирует более ранний ответ, который по праву получил критику за однотипность.
tripleee