Як представити декілька умов у оболонці, якщо оператор?


307

Я хочу представити кілька таких умов:

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 - той, що показує, якщо….

Що не так з цим станом? Я думаю, що щось не так ().


6
Ви не питаєте про умови оболонки, а про умови тестування . Весь вираз у вашому прикладі оцінюється test( [), а не оболонкою. Оболонка оцінює лише статус виходу [.
закінчення


Відповіді:


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, aka [, в основному для зворотної сумісності (оскільки, наприклад, вони були частиною test7-го видання UNIX), але вони явно позначаються як "застарілі" POSIX. Bash (див. Умовні вирази ), здається, випробовує класичні та POSIX значення для -aта -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для забезпечення максимальної надійності та мобільності - яку люди сторінка POSIX сам наводить на думку - ви можете також використовувати подоболочки для угруповання:if ([ $g -eq 1 ] && [ "$c" = "123" ]) || ([ $g -eq 2 ] && [ "$c" = "456" ])
mklement0

Гарне рішення, але мені подобається форма без усіх дужок і дужок: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

На Bash:

if [[ ( $g == 1 && $c == 123 ) || ( $g == 2 && $c == 456 ) ]]

9
Однозначно найкращий підхід у bash. Як і в стороні: в даному випадку навіть не потрібні круглі дужки, тому що всередині [[ ... ]] умовних && на насправді має більш високий пріоритет , ніж ||- в відміну від зовнішнього таких умовних.
mklement0

Чи є спосіб ми з'ясувати, яка умова тут відповідає? Це був перший у круглих дужках чи інший?
Firelord

1
@Firelord: Вам потрібно буде розділити умови в if/ elseоператорі і мати код між thenі fiввести функцію, щоб уникнути його повторення. У дуже простих випадках ви можете використовувати caseоператор з ;&просінням (у Bash 4).
Призупинено до подальшого повідомлення.

1
Яке призначення двох квадратних дужок?
петершаула


37

Використовуючи /bin/bashнаступне, буде працювати:

if [ "$option" = "Y" ] || [ "$option" = "y" ]; then
    echo "Entered $option"
fi

8

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

if [ ! "${somepath}" ] || [ ! "${otherstring}" ] || [ ! "${barstring}" ] ; then

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

2
Це марний підрозділ. { ;}може бути використаний замість цього.
phk

2

Для башти для порівняння струн можна використовувати наступну техніку.

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
Ласкаво просимо до переповнення стека! Це питання шукає пояснення , а не просто робочого коду. Ваша відповідь не дає зрозуміти питаючому, і може бути видалена. Будь ласка , редагувати , щоб пояснити , що викликає спостережувані симптоми.
Toby Speight

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