Якщо інше твердження еквівалентно логічному та && або || і де я віддаю перевагу одному над іншим?


27

Я дізнаюся про структури прийняття рішень і натрапив на ці коди:

if [ -f ./myfile ]
then
     cat ./myfile
else
     cat /home/user/myfile
fi


[ -f ./myfile ] &&
cat ./myfile ||
cat /home/user/myfile

Вони обидва поводяться однаково. Чи є якісь переваги використання одного способу від іншого?



3
Вони не рівноцінні. Дивіться чудову відповідь Ікара. Наприклад, розглянемо випадок, що ./myfile існує, але не читабельний.
AlexP


Відповіді:


26

Ні, конструкція if A; then B; else C; fiі A && B || Cє не є еквівалентною .

З if A; then B; else C; fi, команда Aзавжди оцінюється та виконується (принаймні, робиться спроба її виконання), а потім оцінюється та виконується або команда, Bабо команда C.

З A && B || C, це те ж саме для команд Aі , Bале різні для C: команди Cобчислюється і виконується , якщо або A не вдається або B не вдається.

Припустимо, у вашому прикладі, chmod u-r ./myfileто, незважаючи на [ -f ./myfile ]успіхи, ви будетеcat /home/user/myfile

Моя порада: використовуйте A && Bабо A || Bвсе, що хочете, це залишається легко читати і розуміти, і пастки немає. Але якщо ви маєте на увазі, якщо ... тоді ... інше ... тоді використовуйте if A; then B; else C; fi.


29

Більшості людей легше зрозуміти форму if... then... else....fi

Для цього a && b || c, ви повинні бути впевнені, що bповертається правда. Це є причиною тонких помилок і є вагомою причиною уникати цього стилю. Якщо b не повертає true, це не те саме.

 $ if true; then false ; else echo boom ; fi
 $ true && false || echo boom
 boom

Для дуже коротких тестів та дій, які не мають іншого пункту, скорочена довжина є привабливою, наприклад

 die(){ printf "%s: %s\n" "$0" "$*" >&2 ; exit 1; }

 [ "$#" -eq 2] || die "Needs 2 arguments, input and output"

 if [ "$#" -ne 2 ] ; then
     die "Needs 2 arguments, input and output"
 fi

&&і ||це short circuiting operators, як тільки результат відомий подальші непотрібні тести пропускаються. a && b || cгрупується як (a && b) || c. Перший a- запускається. Якщо він failsвизначений як не повертає статус виходу 0, то група (a && b)відома failі її bне потрібно запускати. ||Чи не знає результат виразу так потрібно виконати c. Якщо це aвдається (повертає нуль), то &&оператор ще не знає результату, a && bтак що bдля виявлення це потрібно. Якщо це bвдається, то a && bвдається, і ||знає, що загальний результат - це успіх, тому не потрібно бігати c. Якщо bне вдасться тоді||досі не знає значення виразу, тому потрібно бігти c.


7

Оператор && виконує наступну команду, якщо попередня команда мала успішне виконання, (повертається код виходу ($?) 0 = логічно вірно).

За формою оцінюється A && B || Cкоманда (або умова) A , і якщо A повертає справжнє (успіх, код виходу 0), тоді виконується команда B. Якщо A не вдасться (таким чином поверне false - код виходу, відмінний від 0) та / або B не вдасться (повернення false ), тоді буде виконана команда C.

Також &&оператор використовується як AND у перевірках стану, і оператор ||працює як АБО у перевірках стану.

Залежно від того, що ви хочете зробити зі своїм сценарієм, форма A && B || Cможе бути використана для перевірки стану, як ваш приклад, або може бути використана для ланцюгових команд і забезпечення ряду команд, які повинні виконуватися, якщо попередні команди мали успішний код виходу 0 .
Саме тому часто можна побачити такі команди , як:
do_something && do_something_else_that_depended_on_something.

Приклади:
apt-get update && apt-get upgrade Якщо оновлення не вдається, оновлення не виконується (має сенс у реальному світі ...).

mkdir test && echo "Something" > test/file
Частина echo "Something"буде виконана лише в тому випадку, якщо mkdir testуспіх і операція повернула вихідний код 0 .

./configure --prefix=/usr && make && sudo make install
Зазвичай зустрічається при складанні завдань для об'єднання необхідних залежних команд разом.

Якщо ви спробуєте реалізувати вище "ланцюги" з if - то - else, вам буде потрібно набагато більше команд і перевірок (і, таким чином, більше коду для запису - більше речей, щоб піти не так) для простого завдання.

Також пам’ятайте, що прикуті команди з && та || читаються оболонкою зліва направо. Можливо, вам доведеться згрупувати команди та перевірки стану за допомогою дужок, щоб залежати наступний крок від успішного виведення деяких попередніх команд. Наприклад, дивіться це:

root@debian:$ true || true && false;echo $?
1 
#read from left to right
#true OR true=true AND false = false = exit code 1=not success

root@debian:$ true || (true && false);echo $?
0 
# true OR (true AND false)=true OR false = true = exit code 0 = success

Або приклад із реального життя:

root@debian:$ a=1;b=1;c=1;[[ $a -eq 1 ]] || [[ $b -eq 1 ]] && [[ $c -eq 2 ]];echo $?
1 
#condition $a = true OR condition b = true AND condition $c = false
#=> yields false as read from left to right, thus exit code=1 = not ok

root@debian:$ a=1;b=1;c=1;[[ $a -eq 1 ]] || [[ $b -eq 1 && $c -eq 2 ]];echo $?
0 
#vars b and c are checked in a group which returns false, 
#condition check of var a returns true, thus true OR false yields true = exit code 0

Майте на увазі, що деякі команди повертають різні коди виходу залежно від виконуваного процесу, або повертають різні коди залежно від їх дії (наприклад, команда GNU diff, повертає 1, якщо два файли відрізняються, і 0, якщо вони відсутні). До таких команд потрібно ставитися обережно в && та || .

Крім того, просто щоб мати загадку разом, майте на увазі об'єднання команд за допомогою ;оператора. З форматом A;B;Cвсі команди виконуватимуться послідовно, незалежно від того, яким був вихідний код команди Aта B.


1

Велика плутанина з цього приводу може бути пов’язана з документацією bash, що називає ці списки AND і OR . Хоча вони логічно схожі на квадратні дужки &&та ||знаходяться всередині квадратних дужок, вони функціонують по-різному.

Деякі приклади можуть ілюструвати це найкраще ...

ПРИМІТКА: Одно- та подвійні квадратні дужки ( [ ... ]і [[ ... ]]) - це самі по собі команди, які виконують порівняння та повертають вихідний код. Вони насправді не потрібні if.

cmda  && cmdb  || cmdc

Якщо cmdaвиходить істина, cmdbвиконується.
Якщо cmdaвиходить false, cmdbНЕ виконується, але cmdcє.

cmda; cmdb  && cmdc  || cmdd

Як cmdaвиходи ігнорується.
Якщо cmdbвиходить істина, cmdcвиконується.
Якщо cmdbвиходить помилково, cmdcНЕ виконується і cmddє.

cmda  && cmdb; cmdc

Якщо cmdaвиходить істина, cmdbвиконується, після чого виконується cmdc.
Якщо cmdaвиходить помилково, cmdbНЕ виконується, але cmdcє.

Так? Чому cmdcстрачується?
Тому що для перекладача крапка з комою ( ;) та новим рядком означають абсолютно те саме. Bash бачить цей рядок коду як ...

cmda  && cmdb
cmdc  

Щоб досягти того, що очікується, ми мусимо укласти cmdb; cmdcвсередині фігурних дужок, щоб зробити їх Складною командою (командою групи) . Додаткове закінчення крапки з комою є лише вимогою { ...; }синтаксису. Отже, ми отримуємо ...

cmda && { cmdb; cmdc; }
Якщо cmdaвиходить істина, cmdbвиконується, після чого виконується cmdc.
Якщо cmdaвиходить помилково, ні cmdbабо cmdcне виконується.
Виконання продовжується наступним рядком.

Використання

Умовні списки команд найкорисніші для якнайшвидшого повернення з функцій і, таким чином, уникнення інтерпретації та виконання багато непотрібного коду. Декілька повернень функцій означають, що потрібно бути нав’язливим щодо того, щоб тримати функції короткими, щоб було легше переконатися, що всі можливі умови покриті.

Ось приклад з деякого запущеного коду ...

fnInit () {
  :
  _fn="$1"
  ### fnInit "${FUNCNAME}" ...
  ### first argument MUST be name of the calling function
  #
  [[ "$2" == "--help-all" ]]  && { helpAll                      ; return 0; }
  ### pick from list of functions
  #
  [[ "$2" == "--note-all" ]]  && { noteAll                      ; return 0; }
  ### pick from notes in METAFILE
  #
  [[ "$2" == "--version"  ]]  && { versionShow "${_fn}" "${@:3}"; return 0; }
  #
  [[ "$2" == "--function" ]]  && {
    isFnLoaded "$3"           && { "${@:3}"                     ; return 0; }
    #
    errorShow functionnotfound "Unknown function:  $3"
    return 0
  }
  ### call any loaded function
  #
  [[ "$2" == "--help" || "$2" == "-h" ]]  && { noteShow "$_fn" "${@:3}"; return 0; }
  ### fnInit "${FUNCNAME}" --help or -h
  #
  return 1
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.