LINUX.ORG.RU

Двойное условие в скрипте bash

 


1

2

Допустим, я хочу проверить вывод какой-то команды на принимаемые значения, но делать что-то только в случае, если условия не выполняются. То есть мне нужно «успешно» завершить работу функции, если выполненная в ней команда вернула «0» или «2». Следующий код не работает (даже если ret = 0 или 2, то вываливает ошибку):

...
ret=$?

if [ "$ret" != "0" ] || [ "$ret" != "2" ]; then
   echo "FAIL"
fi

При использовании круглых скобок вроде отрабатывает, но ругается, что не найдена команда «0» или «2».

Или нужно обязательно городить такое:

...
ret=$?

if [ "$ret" != "0" ]; then
    :
elif [ "$ret" != "2" ]; then
    :
else
    echo "FAIL"
fi

?

★★★★★

Последнее исправление: grem (всего исправлений: 1)

Ничего не понял, но тебе нужно почитать про булеву алгебру, в частности, про законы де Моргана.

anonymous
()

не найдена команда «0» или «2».

if [ "$ret" != "0" ] && [ "$ret" != "2" ]; then
   echo "FAIL"
fi

либо

if [ "$ret" != "0" -a "$ret" != "2" ]; then
   echo "FAIL"
fi
Deleted
()

не найдена команда «0» или «2».

Можно и однострочник:

[ "$ret" != "0" ] && [ "$ret" != "2" ] && echo "FAIL"

Кстати, bash здесь не при чём:

#!/bin/sh
Deleted
()
Последнее исправление: Deleted (всего исправлений: 1)
Ответ на: комментарий от Deleted

это вообще выполнимо одновременно? или в bash всё-таки чуть иначе операторы и/или работают (что я подозреваю)? то есть последовательно выполняются действия?

то есть если [ «$ret» != «0» ] вернёт false, то будет ли выполнено следующее сравнение и, далее, будет ли выполнен последний вывод?

Я проверю, конечно, сейчас не могу. Но мне казалось, что команда после && сработает только если в первой команде возвращается true

grem ★★★★★
() автор топика
Последнее исправление: grem (всего исправлений: 4)
if [[ 2 != "$ret" && 0 != "$ret" ]] ; then  
echo "Fail"; 
fi
raven_cler ★★
()
Ответ на: комментарий от grem

то есть если [ «$ret» != «0» ] вернёт false, то будет ли выполнено следующее сравнение

Тестируй:

#!/bin/sh

ret=$1

if [ "$ret" != "0" ] && [ "$ret" != "2" ]; then
   echo "FAIL1"
fi

if [ "$ret" != "0" -a "$ret" != "2" ]; then
   echo "FAIL2"
fi

[ "$ret" != "0" ] && [ "$ret" != "2" ] && echo "FAIL3"

Deleted
()
Ответ на: комментарий от Deleted

то есть я хочу вернуть FAIL, только если $ret не равно 0 или 2

спасибо, проверю

grem ★★★★★
() автор топика
Последнее исправление: grem (всего исправлений: 1)
Ответ на: комментарий от Deleted

работает, а я упорно использовал || что-то меня на ней переклинило :)

grem ★★★★★
() автор топика

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

imul ★★★★★
()
Ответ на: комментарий от imul

В моём случае будет, так как какое-нибудь значение предыдущая перед ret=$? команда вернёт. В крайнем случае, это будет не хуже текущих принятых реализаций в ebuild, где сначала идёт команда, затем || die «message».

Другое дело, что меня переглючило и я операторы pipeline пытался воспринимать как в большинстве языков, хотя в тоже время знал, как они работали - меня чего-то накрыло :)

grem ★★★★★
() автор топика
Ответ на: комментарий от Deleted

Не надо увлекаться такими однострочниками. Они не совсем так работают в комлексе, как некоторые тут себе представляют, ибо операции || и && они не свертываются, а выполняются последовательно. Следовательно

foo() {
        echo "foo"
        return 0
}

bar() {
        echo "bar"
        return 1
}

foo && bar || echo false
Выдаст
foo
bar
false
а вовсе не
foo
bar

vodz ★★★★★
()
Последнее исправление: vodz (всего исправлений: 1)
Ответ на: комментарий от vodz

выполняются последовательно.

Так в данном случае и нужно последовательно. В других случаях, естественно, не стоит так пользовать.

Deleted
()
Ответ на: комментарий от Deleted

Да, нормальный скриптовый яп обычно годнее.

anonymous
()
Ответ на: комментарий от ubik

верный вариант. (( )) - для чисел и математических операций [[ ]] - для строк

Глупости какие. Если у вас два выражения, одно целочисленное, другое - строковое, то вы всё равно всё заключите в [[ ]], и сразу возникает вопрос, накой (( )) вообще. Нет, я знаю, где (()) удобен, например, в [[ ]] нельзя красиво сделать ((булевое ? целочисленное1 : целочисленное2)). А втыкание (()) внутри [[ ]] зачастую от незнания, что для целочисленных выражений ни $, ни лишние скобки нафиг не нужны. Сравните:

declare -i i1 i2
.....
if [[ i1+i2 -eq REAL+INT ]]; then
...
с реализацией через (()) - насколько пример красивее и нагляднее без кучи скобок.

vodz ★★★★★
()
Последнее исправление: vodz (всего исправлений: 2)
Ответ на: комментарий от vodz

с данном случае оба операнда числовые.

втыкание (()) внутри [[ ]] зачастую от незнания

где вы увидели (()) внутри [[]]?

насколько пример красивее и нагляднее без кучи скобок.

зависит от ситуации

ubik
()
Ответ на: комментарий от grem

он про то, что [ и ] - отдельные бинари, а [[ и ]] - функции баша.

SevikL ★★★★★
()
Ответ на: комментарий от ubik

с данном случае оба операнда числовые.

Ещё раз. Если вам надо проверить условие, то это делается [[ ]], синтаксис для целочисленных там отдельный, удобный и красивый, а самое главное, там можно добавлять условия не только для целочисленных операций, а скажем && $error != «invalid» - просто, расширяемо и читаемо. А вот если вам надо что-то вычислить, то круглые скобки для этого.

vodz ★★★★★
()
Ответ на: комментарий от elonmusk

сложно, нерасширяемо и неудобно

Может стоит прочитать, перед тем как отвечать? Это невозможно, для строчного error.

vodz ★★★★★
()
Ответ на: комментарий от anonymous

Может стоит прочитать, перед тем как отвечать? У ОП-а не сторчный error, а числовой.

Типичный ЛОР, больше двух слов прочитать не могут. У меня было написано: «а самое главное, там можно добавлять условия не только для целочисленных операций, просто и расширяемо». Можете посмотреть в словаре, что означает «расширяемо». И попробовать написать на bash что-нибудь сложнее Hello wordl. И сразу станет понятно, что как надо чуть сложнее, чем одномерные массивы, то есть другие комплексные типы, так сразу приходится работать только со строками, наподобие как awk эмулирует внутри себя многомерные массивы (что не совсем в результате идеально), списки и т. д. А написав проверки условий «по молодёжному, ни так как диды» через (()), вдруг оказывается, что надо всё переписывать как у всех.

А у ТСа вообще error-а не было.

vodz ★★★★★
()
Последнее исправление: vodz (всего исправлений: 3)
Ответ на: комментарий от beastie

А можно ещё и более красиво вот так:

Ничего тут не красивее, а уж что медленнее, так совершенно очевидно.

vodz ★★★★★
()
Ответ на: комментарий от vodz

А написав проверки условий «по молодёжному, ни так как диды» через (())

А в чем «молодежность»? (()) в bash появился раньше [[]]. К тому же (()) есть в POSIX, тогда как [[]] нет. Так что привычка писать простые условия с (()) полезна, если приходится писать переносимый код.

У меня было написано: «а самое главное, там можно добавлять условия не только для целочисленных операций, просто и расширяемо».

Чем

 ((i>1 && i<4)) && [[ err != "invalid" ]] 
хуже
 [[ i -gt 1 && i -lt 4 && err != "invalid" ]] 
в плане простоты и расширяемости? В плане читаемости первый вариант по-моему лучше, хотя при этом оба плохи.

И попробовать написать на bash что-нибудь сложнее Hello wordl. И сразу станет понятно, что как надо чуть сложнее, чем одномерные массивы, то есть другие комплексные типы, так сразу приходится работать только со строками, наподобие как awk эмулирует внутри себя многомерные массивы (что не совсем в результате идеально), списки и т. д.

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

anonymous
()
Ответ на: комментарий от anonymous

А в чем «молодежность»? (()) в bash появился раньше [[]]. К тому же (()) есть в POSIX

Вы путаете с $(()), который раньше был как $[].

Чем ((i>1 && i<4)) && [[ err != «invalid» ]]

Тем, что читать надо тред. Тут уже обсуждалось

vodz ★★★★★
()
Ответ на: комментарий от Deleted

из-за ассоциаций с Си

Это вредная ассоциация, ибо bash свою убогость, справедливости ради, всё же пытается компенсировать, а именно масками в case и в [[]] и даже regex-ами [[ =~ ]], а ваши ассоциации превращают всё в гвозди для молотка, хотя есть отвертка и шурупы тоже.

vodz ★★★★★
()
Ответ на: комментарий от vodz

а уж что медленнее

Поделил на ноль, поздравляю. ;) Даже, если допустить, что так … who cares? Это баш. Читабельность важнее.

beastie ★★★★★
()
Ответ на: комментарий от Deleted

Не шибко пользую, из-за ассоциаций

А зря. Полезная штука. Например для разбора параметров:

#!/bin/sh

while getopts "hn:" args; do
        case $args in
        h)
                echo "usage: $0 [-h] [-n N]"
                ;;
        n)
                echo "n: $OPTARG"
                ;;
        *)
                echo "whut?"
                exit 1
                ;;
        esac
done
beastie ★★★★★
()
Последнее исправление: beastie (всего исправлений: 1)
Ответ на: комментарий от beastie

Читабельность важнее.

Конечно. При коротких условиях городить case — такая читабельность ... Вот буквально час назад написал в новой версии jq.sh в новой функции sort:

case $? in
 2) break;;
 1) continue;;
 0) xchg;;
esac
Похоже? Вроде бы да, но совсем нет, ибо все три и условия и действия — разные. Тут — уместно.

vodz ★★★★★
()
Последнее исправление: vodz (всего исправлений: 2)
Ответ на: комментарий от beastie

Например для разбора параметров:

Разбор параметров - это отдельная тема. В нём case - само собой разумеющееся. Но в остальных местах уже не пользую.

Deleted
()
Ответ на: комментарий от vodz

Вы путаете с $(()), который раньше был как $[].

Насчет POSIX действительно перепутал. Но в bash (()) был на пару лет раньше [[]]. Не то, чтобы это что-то кардинально меняло, просто факт.

Тем, что читать надо тред. Тут уже обсуждалось

Обсуждалось наивное использование конструкций вида A && B || C вместо if A; then B; else C; fi для сокращения кода. Но это не значит, что нужно избегать использования && и || везде (учитывая еще и то, что в условии очень часто может быть не только сравнение чисел или строк, но и команда). Нужно знать и всегда помнить, как они работают. Точно так же, как, например, нужно знать и помнить, что в [[]] есть два разных набора операторов сравнения для целых чисел и строк. В обоих случаях незнание приведет к ошибкам и неправильному пониманию кода.

anonymous
()
Ответ на: комментарий от anonymous

Не то, чтобы это что-то кардинально меняло, просто факт.

Ну на самом деле не так всё было. (()) в виде let был очень давно. Но удобств от let было мало, результат надо было брать из $(let expr). Потому bash первый пошёл на «улучшения», но сломал совместимость, теперь let и (( )) - полный alias, без вывода на stdout.

В обоих случаях незнание приведет к ошибкам и неправильному пониманию кода.

Какая напыщенная, непонятно для кого, лекция. Если вы посмотрите мой код, например, из последней моей новости, то вы там найдёте и конструкции ((выражения)) && действия, и case с только двумя выборами и всё что тут обсуждали. Вот только там это по делу.

vodz ★★★★★
()
Последнее исправление: vodz (всего исправлений: 1)
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.