Це тест або [або [[більш портативний як між оболонками bash, так і між іншими оболонками?

Я бачу, що можу

$ [ -w /home/durrantm ] && echo "writable"
writable

або

$ test -w /home/durrantm && echo "writable"
writable

або

$ [[ -w /home/durrantm ]] && echo "writable"
writable

I like using the third syntax. Are they equivalent in all ways and fабо all negative and edge cases? Are there any differences in pабоtability, e.g. between bash on Ubuntu and on OS X або older/newer bash versions, e.g. befабоe/after 4.0 and do they both expand expressions the same way?

42
Для […] vs [[…]] vs test… є більш повні відповіді в це в основному повторюється запитання .
додано Автор Valters Vingolds, джерело
Для конкретного питання тестування на записуваність файлу див. Також Як неінвазивно перевірити доступ для запису до файлу/a>
додано Автор G-Man, джерело
Існує приказка: "немає портативного коду, тільки код, який був перенесений". Моя порада щодо цього: скористайтеся найбільш читаною формою (можливо [[...]]) і спробуйте її на всіх платформах, які ви хочете підтримати. Існує не так багато в тому, щоб затушувати ваші сценарії, тому вони працюють на стародавніх платформах, які ні ви, ні ваша цільова аудиторія використовуєте. Це буде просто зробити ваш код важко читати, ввести непотрібні помилки і, можливо, навіть проблеми з безпекою (як це було для openssl).
додано Автор Sam Gamoran, джерело

7 Відповіді

Так, є відмінності. Найбільш портативними є test або [] . Вони є частиною специфікації POSIX a>.

Конструкція if ... fi також є

POSIX і має бути повністю портативним.

[[]] - функція ksh , яка також присутній у деяких версіях bash ( усі сучасні , в zsh і, можливо, в інших, але немає в sh або тире або інших простих оболонках.

Отже, щоб перенести сценарії, використовуйте [] , test if ... fi .

34
додано
Одна цікава особливість [[ полягає в тому, що розширення параметрів не обов'язково цитувати: [[-f $ file]] подія працює, якщо $ file містить пробільні символи.
додано Автор Matt Enright, джерело
@helpermethod також, вбудовані регулярні вирази [[$ a = ~ ^ reg. * exp. * $ ']]
додано Автор GnP, джерело
Зауважимо, bash і zsh підтримують [[ протягом дуже довгого часу ( bash додав його в наприкінці 90-х, zsh не пізніше 2000 року, і я був би здивований, якщо він коли-небудь не матиме підтримки), тому ви навряд чи знайдете версію без [[. Набагато більше шансів зустріти іншу оболонку, сумісну з POSIX (наприклад, тире ).
додано Автор Luke Smith, джерело

[ is synonym of the test command and it is simultaneously a bash builtin and separate command. But [[ is a bash keyword and works in some versions only. So for reasons of portability you are better off using single [] or test

[ -w "/home/durrantm" ] && echo "writable"
28
додано

Please note, that [] && cmd Не те ж самеif .. fi construction.

Sometimes its behaviour its pretty similar and you can use [] && cmd instead of if .. fi. But only sometimes. If you have more then one command to execute if condition or you need if .. else .. fi be careful and whatch the logic.

Кілька прикладів:

[ -z "$VAR" ] && ls file || echo wiiii

Не те ж саме

if [ -z $VAR ] ; then
  ls file
else
  echo wiii
fi

тому, що якщо ls не буде виконано, echo буде виконано, що не відбудеться з if .

Інший приклад:

[ -z "$VAR" ] && ls file && echo wiii

Не те ж саме

if [ -z "$VAR" ] ; then
   ls file
   echo $wiii
fi

хоча ця конструкція буде діяти так само

[ -z "$VAR" ] && { ls file ; echo wiii ; }

Зверніть увагу, ; після того, як ехо важливо і має бути там.

Отже, відновлення заяви вище можна сказати

[] && cmd == if first command is successful then execute the next one

if .. fi == if condition (which may be the test command as well) then execute command(s)

Отже, для перенесення між [ і [[ використовуйте лише [

if is POSIX compatible. So if you have to choose between [ and if choose looking at your task and expected behaviour.

19
додано
Я проголосував, тому що а) це головним чином хороша відповідь, але це відповідь на інше питання. б) ви подаєте [ і if як заступники, але вони не є. Насправді це && , що замінює if . [ виконує деякий код і повертає статус, подібно до ls і grep . if виконується в залежності від статусу повернення команди (оператора), наданої після того, як це може бути будь-яка команда (оператор). && виконує наступний оператор, лише якщо попередній повернув 0, подібно до простого if..then..fi .
додано Автор GnP, джерело
@rush ви маєте рацію, я пропустив історію редагування. Мої вибачення. Як я вже сказав, це головним чином хороша відповідь, тому я виправив свій голос.
додано Автор GnP, джерело
Я, напевно, просто щільний, але я не бачу, яка різниця буде ... чи не могли б ви дати приклад, коли ці дві конструкції дадуть різні результати?
додано Автор evilsoup, джерело
@evilsoup, оновлено. Можливо, я не найкращий пояснювач, хоча сподіваюся, що тепер це буде зрозуміло.
додано Автор agrublev, джерело
@gnp, добре. а) будь ласка, перевірте коментар від Майкла. Існує коротке пояснення, що спочатку було інше питання. Я просто залишив відповідь на історію. b) Відповідь була в основному про різницю між && .. || та if .. else .. fi .
додано Автор agrublev, джерело
Зроблено питання про [vs [[vs тест, а не про те, якщо ... fi vs &&, щоб зробити це ОДИН питання. На жаль, це робить цю відповідь невідповідною. Вибачте за те, що спочатку не було поставлено запитання, що призвело до цього. Живи і (намагайся) вчитися. :)
додано Автор ExpelledFromParadise, джерело
Дуже гарні точки, поспішайте. Я вважаю, що безпечною формою вашого першого прикладу буде: [-z "$ VAR"] && {ls file; true; } || echo wiiii . Це трохи більш детально, але все ще коротше, ніж конструкція if ... fi .
додано Автор DirkGently, джерело

It's actually the && that is replacing the if, not the test: an if statement in shell scripting tests whether a command returned a "successful" (zero) exit status; in your example, the command is [.

Отже, є дві факти, які ви тут змінюєте: команда, яка використовується для запуску тесту, і синтаксис, який використовується для виконання коду на основі результату цього тесту.

Команди тестування:

  • test is a standardised command for evaluating properties of strings and files; in your example, you are running the command test -w /home/durrantm
  • [ is an alias of that command, equally standardised, which has a mandatory last argument of ] in order to look like a bracketed expression; don't be fooled, it's still just a command (you may even find that your system has a file called /bin/[)
  • [[ is an extended version of the test command built into some shells, but not part of the same POSIX standard; it includes extra options which you are not using here

Умовні вирази:

  • The && operator (standardised here) performs a logical AND operation, by evaluating two commands and returning 0 (which represents true) if they both return 0; it will only evaluate the second command if the first one returned zero, so it can be used as a simple conditional
  • The if ... then ... fi construct (standardised here) uses the same method of judging "truth", but allows for a compound list of statements in the then clause, rather than the single command afforded by an && short-circuit, and provides elif and else clauses, which are hard to write using only && and ||. Note that there are no brackets around the condition in an if statement.

Таким чином, наступні всі однаково портативні, і цілком еквівалентні, візуалізації вашого прикладу:

  • test -w /home/durrantm && echo "writable"
  • [ -w /home/durrantm ] && echo "writable"
  • if test -w /home/durrantm; then echo "writable"; fi
  • if [ -w /home/durrantm ]; then echo "writable"; fi

Незважаючи на нестандартний характер [[:

  • [[ -w /home/durrantm ]] && echo "writable"
  • if [[ -w /home/durrantm ]]; then echo "writable"; fi
9
додано
Так, я зняв частину if .... fi, щоб зробити це питання 1.
додано Автор ExpelledFromParadise, джерело

Якщо вам потрібна портативність за межами світу, подібного до Bourne, то:

test -w /home/durrantm && echo writable

є найбільш портативним. Він працює в оболонках сімейства Bourne, csh і rc .

test -w /home/durrantm && echo "writable"

видасть "writable" замість запису в оболонках сім'ї rc ( rc , es , akanga , де " не є спеціальним).

[ -w /home/durrantm ] && echo writable

не працюватиме в оболонках сімей csh або rc у системах, які не мають команди [ в $ PATH (деякі з них, як відомо, мають test , але не його [ псевдонім).

if [ -w /home/durrantm ]; then echo writabe; fi

працює тільки в снарядах сім'ї Борна.

[[ -w /home/durrantm ]] && echo writable

працює тільки в ksh (де виник), zsh і bash (всі 3 у сім'ї Bourne).

Ніхто не працюватиме в оболонці fish , де вам потрібно:

[ -w /home/durrantm ]; and echo writable

або:

if [ -w /home/durrantm ]; echo writable; end
7
додано

Для перенесення використовуйте test / [. Але якщо вам не потрібна портативність, заради розуму себе та інших читачів вашого сценарію використовуйте [[. :)

Також див.

7
додано

My most important reason for choosing either if foo; then bar; fi or foo && bar is whether the exit status of the whole command is important.

порівняти:

#!/bin/sh
set -e
foo && bar
do_baz

з:

#!/bin/sh
set -e
if foo; then bar; fi
do_baz

Ви можете подумати, що вони роблять те ж саме; однак, якщо foo виходить з ладу (або є помилковим, залежно від вашої точки зору), то в першому прикладі do_baz не буде виконано, оскільки скрипт завершиться ... інструктує оболонку негайно вийти, якщо будь-яка команда повертає помилковий статус. Дуже корисно, якщо ви виконуєте такі дії:

cd /some/directory
rm -rf *

Ви не хочете, щоб сценарій продовжував працювати, якщо cd збій з будь-якої причини.

0
додано
Ніякий невдалий код foo не може перервати сценарій у будь-якому випадку. Це особливий випадок для set-e (коли команда оцінюється як умова (ліворуч від деяких &&/|| або в умовах if/while/till/elsif ...). якщо bar не вийде з оболонки в обох випадках.
додано Автор Stéphane Chazelas, джерело
Ви хочете cd/some/directory && rm -rf - * або cd/some/каталог || Вхід; rm -rf - * (все ще не видаляє прихованих файлів). Мені особисто не подобається ідея використання set -e як виправдання для того, щоб не намагатися написати правильний код.
додано Автор Stéphane Chazelas, джерело