Как представить несколько условий в оболочке, если заявление?

308

Я хочу представить несколько условий, как это:

if [ ( $g -eq 1 -a "$c" = "123" ) -o ( $g -eq 2 -a "$c" = "456" ) ]   
then  
    echo abc;  
else  
    echo efg;   
fi  

но когда я выполняю скрипт, он показывает

syntax error at line 15: `[' unexpected, 

где строка 15 показывает, если ....

Что не так с этим условием? Я думаю, что-то не так с ().

user389955
источник
6
Вы спрашиваете не об условиях оболочки, а об условиях тестирования . Все выражение в вашем примере оценивается test( [), а не оболочкой. Оболочка оценивает только состояние выхода [.
ceving
См. Также stackoverflow.com/questions/16203088/…
tripleee

Ответы:

381

Классическая техника (метасимволы экранирования):

if [ \( "$g" -eq 1 -a "$c" = "123" \) -o \( "$g" -eq 2 -a "$c" = "456" \) ]
then echo abc
else echo efg
fi

Я заключил ссылки $gв двойные кавычки; это хорошая практика, в общем. Строго говоря, скобки не нужны, потому что приоритет -aи -oделает его правильным даже без них.

Обратите внимание , что -aи -oоператоры являются частью спецификации POSIX для testака [, в основном для обратной совместимости (так как они были частью testв 7 - е издание UNIX, например), но они явно помечены как «устаревающих» по стандарту POSIX. Bash (см. Условные выражения ), кажется, вытесняет классические значения -aи значения POSIX для -oсвоих собственных альтернативных операторов, которые принимают аргументы.


С некоторой осторожностью вы можете использовать более современный [[оператор, но помните, что версии в Bash и Korn Shell (например) не обязательно должны быть идентичными.

for g in 1 2 3
do
    for c in 123 456 789
    do
        if [[ ( "$g" -eq 1 && "$c" = "123" ) || ( "$g" -eq 2 && "$c" = "456" ) ]]
        then echo "g = $g; c = $c; true"
        else echo "g = $g; c = $c; false"
        fi
    done
done

Пример запуска с использованием Bash 3.2.57 на Mac OS X:

g = 1; c = 123; true
g = 1; c = 456; false
g = 1; c = 789; false
g = 2; c = 123; false
g = 2; c = 456; true
g = 2; c = 789; false
g = 3; c = 123; false
g = 3; c = 456; false
g = 3; c = 789; false

Вам не нужно заключать переменные в кавычки, [[как вы это делаете, [потому что это не отдельная команда, как это [делается.


Разве это не классический вопрос?

Я бы так и подумал. Однако есть и другая альтернатива, а именно:

if [ "$g" -eq 1 -a "$c" = "123" ] || [ "$g" -eq 2 -a "$c" = "456" ]
then echo abc
else echo efg
fi

В самом деле, если вы прочитали рекомендации по «портативной оболочке» для autoconfинструмента или связанных пакетов, то эти обозначения - с использованием ||«и &&» - это то, что они рекомендуют. Я полагаю, вы могли бы даже пойти так далеко:

if [ "$g" -eq 1 ] && [ "$c" = "123" ]
then echo abc
elif [ "$g" -eq 2 ] && [ "$c" = "456" ]
then echo abc
else echo efg
fi

Там, где действия так же банальны, как и эхо, это неплохо. Когда блок действий, который должен повторяться, состоит из нескольких строк, повторение слишком болезненное, и предпочтительна одна из более ранних версий - или вам нужно заключить действия в функцию, которая вызывается в разных thenблоках.

Джонатан Леффлер
источник
9
Хорошо знать, что экранированные скобки работают; как в стороне: в данном конкретном случае, скобки даже не нужны, потому что на -aсамом деле имеет более высокий приоритет , чем -o( в отличие , &&и ||в оболочке - однако, внутри bash [[ ... ]] условных , && также имеет более высокий приоритет , чем ||). Если вы хотите избежать -aи -oдля максимальной надежности и переносимости - что предлагает самаif ([ $g -eq 1 ] && [ "$c" = "123" ]) || ([ $g -eq 2 ] && [ "$c" = "456" ])
справочная
Хорошее решение, но мне нравится форма без скобок и скобок:if test "$g" -eq 1 -a "$c" = "123" || test "$g" -eq 2 -a "$c" = "456"; then ...
Mogens TrasherDK
Я немного опоздал к этому, но это все еще лучший результат поиска, поэтому я просто хотел бы отметить, что использование &&или ||также предпочтительнее, поскольку оно ведет себя больше как условные выражения в других языках и позволяет вам создавать условия короткого замыкания. Например: if [ -n "${check_inodes}" ] && [ "$(stat -f %i "/foo/bar")" = "$(stat -f %i "/foo/baz")" ]. Делая это таким образом, если check_inodesоно пустое, вы избегаете двух обращений к stat, тогда как более сложное условие теста должно обрабатывать все аргументы перед выполнением (что также может привести к ошибкам, если вы забудете об этом поведении).
Харавикк
182

В Баше:

if [[ ( $g == 1 && $c == 123 ) || ( $g == 2 && $c == 456 ) ]]
Приостановлено до дальнейшего уведомления.
источник
9
Определенно лучший подход в bash. В качестве отступления: в данном конкретном случае скобки даже не нужны, потому что внутри [[ ... ]] условных условий на && самом деле более высокий приоритет, чем ||- в отличие от таких условных выражений.
mklement0
Есть ли способ узнать, какое условие здесь соответствует? Это был первый в круглых скобках или другой?
Повелитель огня
1
@Firelord: вам нужно было бы разделить условия на оператор if/ elseи иметь код между ними thenи fiвставить функцию, чтобы избежать ее повторения. В очень простых случаях вы можете использовать caseоператор с ;&падением (в Bash 4).
Приостановлено до дальнейшего уведомления.
1
Какова цель двух квадратных скобок?
peterchaula
2
@peter: Пожалуйста, смотрите мой ответ здесь , Условные конструкции в руководстве по Bash и BashFAQ / 31 .
Приостановлено до дальнейшего уведомления.
37

Использование /bin/bashследующего будет работать:

if [ "$option" = "Y" ] || [ "$option" = "y" ]; then
    echo "Entered $option"
fi
Sunitha
источник
8

Будьте осторожны, если в строковых переменных есть пробелы, и вы проверяете существование. Обязательно процитируйте их правильно.

if [ ! "${somepath}" ] || [ ! "${otherstring}" ] || [ ! "${barstring}" ] ; then
orkoden
источник
1
когда мы используем фигурные скобки после символа доллара !!
YouAreAwesome
6
$ g=3
$ c=133
$ ([ "$g$c" = "1123" ] || [ "$g$c" = "2456" ]) && echo "abc" || echo "efg"
efg
$ g=1
$ c=123
$ ([ "$g$c" = "1123" ] || [ "$g$c" = "2456" ]) && echo "abc" || echo "efg"
abc
ghostdog74
источник
2
Это бесполезная подделка. { ;}может быть использован вместо
23.09.16
2

В bash для сравнения строк вы можете использовать следующую технику.

if [ $var OP "val" ]; then
    echo "statements"
fi

Пример:

var="something"
if [ $var != "otherthing" ] && [ $var != "everything" ] && [ $var != "allthings" ]; then
    echo "this will be printed"
else
    echo "this will not be printed"
fi
Ашан Приядаршана
источник
0
#!/bin/bash

current_usage=$( df -h | grep 'gfsvg-gfslv' | awk {'print $5'} )
echo $current_usage
critical_usage=6%
warning_usage=3%

if [[ ${current_usage%?} -lt ${warning_usage%?} ]]; then
echo OK current usage is $current_usage
elif [[ ${current_usage%?} -ge ${warning_usage%?} ]] && [[ ${current_usage%?} -lt ${critical_usage%?} ]]; then
echo Warning $current_usage
else
echo Critical $current_usage
fi
Дебджоти Банерджи
источник
3
Добро пожаловать в стек переполнения! Этот вопрос ищет объяснение , а не просто рабочий код. Ваш ответ не дает понять вопрос, и может быть удален. Пожалуйста, измените, чтобы объяснить, что вызывает наблюдаемые симптомы.
Тоби Спейт
0

Вы также можете связать более 2 условий:

if [ \( "$1" = '--usage' \) -o \( "$1" = '' \) -o \( "$1" = '--help' \) ]
then
   printf "\033[2J";printf "\033[0;0H"
   cat << EOF_PRINT_USAGE

   $0 - Purpose: upsert qto http json data to postgres db

   USAGE EXAMPLE:

   $0 -a foo -a bar



EOF_PRINT_USAGE
   exit 1
fi
Йордан Георгиев
источник