bash якщо не кілька умов без підзарядки?


23

Я хочу об'єднати кілька умов у оболонці if оператор та заперечувати комбінацію. У мене є такий робочий код для простого поєднання умов:

if [ -f file1 ] && [ -f file2 ] && [ -f file3 ] ; then
  # do stuff with the files
fi

Це чудово працює. Якщо я хочу заперечувати це, я можу використовувати наступний робочий код:

if ! ( [ -f file1 ] && [ -f file2 ] && [ -f file3 ] ) ; then
  echo "Error: You done goofed."
  exit 1
fi
# do stuff with the files

Це також працює як очікувалося. Однак мені здається, що я не знаю, що насправді мають круглі дужки. Я хочу використати їх лише для групування, але чи це насправді нерест нижньої оболонки? (Як я можу сказати?) Якщо так, чи є спосіб групувати умови, не будуючи нересурс?


Я думаю, я міг би також використовувати булеву логіку для визначення еквівалентного виразу: if ! [ -f file1 ] || ! [ -f file 2 ] || ! [ -f file3 ] ; thenале мені хотілося б більш загальної відповіді.
Wildcard

Ви також можете if [[ ! -f file1 ]] && [[ ! -f file2 ]]; then
заперечити

1
Так, я просто перевірив, if [ ! 1 -eq 2 ] && [ ! 2 -eq 3 ]; then echo yep; fiі це працює. Я завжди завжди пишу тести з подвійними дужками як питання звички. Крім того, щоб переконатися, що це не так bash, я додатково перевірив, if /bin/test ! 1 -eq 2 && /bin/test ! 2 -eq 3 ; then echo yep; fiі це також працює.
DopeGhoti

1
@Wildcard - видалить [ ! -e file/. ] && [ -r file ]каталоги. заперечуйте це як завгодно. Звичайно, саме це і -dробить.
mikeserv

1
Питання, пов’язані з цим (схоже, але не так багато обговорень та відповідей, не дуже корисних): unix.stackexchange.com/q/156885/135943
Wildcard

Відповіді:


32

Вам потрібно використовувати { list;}замість (list):

if ! { [ -f file1 ] && [ -f file2 ] && [ -f file3 ]; }; then
  : do something
fi

І те й інше групами команд , але { list;}виконує команди в поточному середовищі оболонки.

Слід зазначити, що ;в { list;}необхідності розмежувати список з }зворотного слова, ви можете використовувати інший роздільник , а також. Пробіл (або інший роздільник) після {також потрібно.


Дякую! Щось, що спочатку мене спонукало (оскільки я використовую фігурні брекети awkчастіше, ніж у bash), - це потреба у пробілі після відкритого фігурного дужка. Ви згадуєте "ви можете використовувати й інші роздільники"; будь-які приклади?
Wildcard

@Wildcard: Так, пробіл після відкритого фігурного дужки потрібен. Прикладом для іншого роздільника є новий рядок, оскільки ви часто хочете, щоб ви визначали функцію.
cuonglm

@don_crissti: У zsh, ви можете використовувати пробіли
cuonglm

@don_crissti: &також є роздільником, ви також можете використовувати { echo 1;:&}кілька нових рядків.
cuonglm

2
Ви можете використовувати будь-який метасимвол цитату з ручного they must be separated from list by whitespace or another shell metacharacter.Баша вони: metacharacter: | & ; ( ) < > space tab . Для цього конкретного завдання я вважаю, що & ; ) space tabспрацює будь-яка людина . .... .... З zsh (як коментар) each sublist is terminated by &', &!', or a newline.

8

Ви можете повністю використовувати testфункціонал, щоб досягти того, що ви хочете. З чоловічої сторінки test:

 ! expression  True if expression is false.
 expression1 -a expression2
               True if both expression1 and expression2 are true.
 expression1 -o expression2
               True if either expression1 or expression2 are true.
 (expression)  True if expression is true.

Так ваш стан може виглядати так:

if [ -f file1 -a -f file2 -a -f file3 ] ; then
    # do stuff with the files
fi

Для заперечення використовуйте сковані дужки:

if [ ! \( -f file1 -a -f file2 -a -f file3 \) ] ; then
    echo "Error: You done goofed."
    exit 1
fi
# do stuff with the files

6

Щоб перенести заперечення складної умовної оболонки, ви повинні або застосувати закон Де Моргана, і засунути заперечення всередину [викликів ...

if [ ! -f file1 ] || [ ! -f file2 ] || [ ! -f file3 ]
then
    # do stuff
fi

... або ви повинні використовувати then :; else...

if [ -f file1 ] && [ -f file2 ] && [ -f file3 ]
then :
else
  # do stuff
fi

if ! commandне є портативно доступним і не є [[.

Якщо вам не потрібна повна портативність, не пишіть сценарій оболонки . Ви насправді більше шансів знайти /usr/bin/perlна випадково вибраному Unix, ніж ви bash.


1
!це POSIX, який сьогодні є портативним. Навіть системи, які все ще постачаються з оболонкою Bourne, також мають POSIX sh десь у файловій системі, яку ви можете використовувати для інтерпретації свого стандартного синтаксису.
Стефан Шазелас

@ StéphaneChazelas Це технічно правда, але, на моєму досвіді, легше переписати сценарій в Perl, ніж боротися з клопотами з пошуку та активації середовища, сумісного з POSIX-оболонками /bin/sh. Сценарії Autoconf виконуються так, як ви пропонуєте, але я думаю, що всі ми знаємо, наскільки мало схвалення.
zwol

5

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

if  ! for f in file1 file2 file3
      do  [ -f "$f" ] || ! break
      done
then  : do stuff
fi

... як це показано в іншому місці { :;}, немає ніяких труднощів, пов'язаних із введенням складних команд ...

Зауважте, що наведені вище (як правило) тести для звичайних файлів. Якщо ви шукаєте лише наявні , читабельні файли, які не є каталогами:

if  ! for f in file1 file2 file3
      do  [ ! -d "$f" ] && 
          [   -r "$f" ] || ! break
      done
then  : do stuff
fi

Якщо вам все одно, чи це каталоги, чи ні:

if  ! command <file1 <file2 <file3
then  : do stuff
fi

... працює для будь-якого читаного , доступного файлу, але, швидше за все, висить для авторів fifos w / out.


2

Це не повна відповідь на ваше головне питання, але я помітив, що ви згадуєте складне тестування (читабельний файл) у коментарі; наприклад,

if [ -f file1 ] && [ -r file1 ] && [ -f file2 ] && [ -r file2 ] && [ -f file3 ] && [ -r file3 ]

Ви можете трохи закріпити це, визначивши функцію оболонки; наприклад,

readable_file()
{
    [ -f "$1" ]  &&  [ -r "$1" ]
}

Додайте поводження з помилками (наприклад, [ $# = 1 ]) за смаком. Перше ifтвердження вище, тепер може бути скорочене

if readable_file file1  &&  readable_file file2  &&  readable_file file3

і ви можете ще більше скоротити це, скоротивши назву функції. Так само ви можете визначити not_readable_file()(або nrfкоротко) та включити заперечення у функцію.


Приємно, але чому б просто не додати цикл? readable_files() { [ $# -eq 0 ] && return 1 ; for file in "$@" ; do [ -f "$file" ] && [ -r "$file" ] || return 1 ; done ; } (Відмова: Я перевірив цей код, і він працює, але він не був одноколірним. Я додав крапки з комою для розміщення тут, але не перевіряв їх розміщення.)
Wildcard

1
Гарна думка. Я б висловив незначну стурбованість тим, що вона приховує більше логіки управління (сполучення) у функції; хтось, хто читає сценарій і не бачить if readable_files file1 file2 file3, не знатиме, чи виконувала цю функцію AND чи АБО - хоча (а) я думаю, це досить інтуїтивно, (b) якщо ви не поширюєте сценарій, це досить добре, якщо ви його розумієте, та (c) оскільки я запропонував скоротити ім’я функції до незрозумілості, я не маю можливості говорити про читабельність. … (Продовжував)
G-Man каже: «Відновіть Моніку»

1
(Продовження) ... До речі, функція відмінно, як ви написали, але ви можете замінити for file in "$@" ; doз for file do- значення по замовчуванням дорівнює in "$@", і (кілька контр-інтуїтивно) ;не тільки не потрібна , але на самому справі НЕ рекомендується.
G-Man каже: "Відновіть Моніку"

0

Ви також можете заперечити всередині тестів на дужки, щоб повторно використовувати свій початковий код:

if [[ ! -f file1 ]] || [[ ! -f file2 ]] || [[ ! -f file3 ]] ; then
  # do stuff with the files
fi

2
Вам потрібно ||замість&&
cuonglm

Тест не " жодного з файлів немає", тест - " жодного з файлів немає". Використання ||виконує присудок, якщо жодного з файлів немає, не, якщо і тільки якщо всі файли відсутні. NOT (A AND B AND C) - це те саме, що (NOT A) AND (NOT B) AND (NOT C); дивіться оригінальне запитання.
DopeGhoti

@DopeGhoti, cuonglm вірно. Це якщо немає (всі файли там) так що, коли ви розбити його таким чином , що вам потрібно ||замість &&. Дивіться мій перший коментар нижче самого мого запитання.
Wildcard

2
@DopeGhoti: not (a and b)те саме, що (not a) or (not b). Дивіться en.wikipedia.org/wiki/De_Morgan%27s_laws
cuonglm

1
Це має бути четвер. Я ніколи не міг завісити четверг. Виправлено, і я залишу ногою рот для нащадків.
DopeGhoti

-1

Я хочу використати їх лише для групування, але чи це насправді нерест нижньої оболонки? (Як я можу це сказати?)

Так, команди в межах (...)виконуються в підрозділі.

Щоб перевірити, чи перебуваєте ви в нижній частині корпусу, ви можете порівняти PID вашої поточної оболонки з PID інтерактивної оболонки.

echo $BASHPID; (echo $BASHPID); 

Приклад виведення, демонструючи, що в дужках породжений піддіаграма і фігурні дужки не:

$ echo $BASHPID; (echo $BASHPID); { echo $BASHPID;}
50827
50843
50827
$ 


@heemayl, це інша частина мого запитання - чи я можу побачити або продемонструвати на екрані факт появи подій в нижній частині? (Це справді може бути окремим питанням, але було б добре знати тут.)
Уайлдкард

1
@heemayl Ви маєте рацію! Я зробив, echo $$ && ( echo $$ )щоб порівняти PID, і вони були однакові, але перед тим, як я подав коментар, у якому ви сказали, що ви неправі, я спробував ще одне. echo $BASHPID && ( echo $BASHPID )дає різні ПІД
Девід Кінг

1
@heemayl echo $BASHPID && { echo $BASHPID; }дайте той самий PID, як ви запропонували. Дякую за освіту
Девід Кінг

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