Термінальний оператор (? :) в Баші


442

Чи є спосіб зробити щось подібне

int a = (b == 5) ? c : d;

використовуючи Bash?


1
@ відповідь dutCh показує, що bashмає щось подібне до "потрійного оператора", проте в bashцьому це називається "умовний оператор" expr?expr:expr(див. man bashперехідний розділ "Арифметична оцінка"). Майте на увазі, що bash"умовний оператор" є хитрим і має деякі добутки.
Тревор Бойд Сміт

У Bash є потрійний оператор для цілих чисел, і він працює всередині арифметичного виразу ((...)). Дивіться арифметику оболонки .
codeforester

1
Як згадував @codeforester, потрійний оператор працює з арифметичним розширенням $(( ))та арифметичною оцінкою (( )). Дивіться також https://mywiki.wooledge.org/ArithmeticExpression.
Кай

Відповіді:


490

потрійний оператор ? :- це просто коротка формаif/else

case "$b" in
 5) a=$c ;;
 *) a=$d ;;
esac

Або

 [[ $b = 5 ]] && a="$c" || a="$d"

103
Зауважте, що =оператор перевіряє рівність рядків, а не числову рівність (тобто [[ 05 = 5 ]]хибність). Якщо ви хочете числове порівняння, використовуйте -eqзамість цього.
Гордон Девіссон

10
Це більш коротка форма дляif/then/else
vol7ron

15
Це геніальний спосіб використовувати поведінку короткого замикання, щоб отримати ефект потрійного оператора :) :) :)
mtk

6
чому [[і]]? він працює так само добре: [$ b = 5] && a = "$ c" || a = "$ d"
kdubs

83
У cond && op1 || op2конструкції є притаманна помилка: якщо op1з будь-якої причини є ненульовий статус виходу, результат тихо стане op2. if cond; then op1; else op2; fiтакож є одна лінія і не має цього дефекту.
ivan_pozdeev

375

Код:

a=$([ "$b" == 5 ] && echo "$c" || echo "$d")

58
це краще, ніж інші ... Суть про третинний оператор полягає в тому, що це оператор, отже, належний контекст є у виразі, отже, він повинен повертати значення.
nic ferrier

2
Це найбільш стислий спосіб. Майте на увазі, що якщо частина з echo "$c"- це псевдонімна, багаторядкова команда (наприклад echo 1; echo 2), вам слід укласти її в дужки.
Метт

2
Це також захопить будь-який результат тестованої команди (так, у цьому конкретному випадку ми «знаємо», що вона не дає жодної).
ivan_pozdeev

8
Ця ланцюжок операторів веде себе як потрійний оператор лише в тому випадку, якщо ви впевнені, що команда після &&не матиме статус нульового виходу. В іншому випадку a && b || c буде "несподівано" працювати, cякщо aвдасться, але bне вдасться.
чепнер

155

Якщо умова лише перевіряє, чи встановлена ​​змінна, існує ще коротша форма:

a=${VAR:-20}

призначить aзначення VARif, якщо VARвоно встановлено, інакше воно призначить йому значення за замовчуванням 20- це також може бути результатом вираження.

Як зазначає Алекс у коментарі, такий підхід технічно називається "Розширення параметрів".


6
У випадку передачі параметра рядка з дефісами в ньому мені довелося використовувати лапки: a=${1:-'my-hyphenated-text'}
saranicole

1
посилання для ледачих - є додаткові оператори, ніж просто заміна ( :-)
Джастін Вробель

10
@JustinWrobel - на жаль, немає подібного синтаксису ${VAR:-yes:-no}.
Кен Вільямс

76
if [ "$b" -eq 5 ]; then a="$c"; else a="$d"; fi

cond && op1 || op2Вираз пропонується в інших відповідях має властиву помилку: якщо op1має ненульовий статус завершення, op2мовчки стає результатом; помилка також не потрапить у -eрежим. Таким чином, цей вислів тільки безпечно використовувати , якщо op1не може потерпіти невдачу (наприклад, :, trueякщо вбудовані ним , або надання значення змінних без будь - яких операцій , які можуть не (як поділ і виклики ОС)).

Зверніть увагу на ""цитати. Перша пара запобіжить помилку синтаксису, якщо $bвона порожня або має пробіли. Інші заважають перекладати весь пробіл в єдині пробіли.


1
Я також бачив префіксацію, я вважаю, що [ "x$b" -eq "x" ]використовується для тестування порожнього рядка конкретно. Мені подобається використовувати синтаксиси, які використовує zsh ("ПАРАМЕТРОВЕ РОЗШИРЕННЯ" на сторінці zshexpn), але я не знаю багато про їх переносимість.
Джон П


46
(( a = b==5 ? c : d )) # string + numeric

31
Це добре для числових порівнянь та призначень, але це дасть непередбачувані результати, якщо ви використовуєте його для порівняння рядків і призначень .... (( ))трактує будь-які / всі рядки як0
Peter.O

3
Про це можна також написати:a=$(( b==5 ? c : d ))
joeytwiddle

33
[ $b == 5 ] && { a=$c; true; } || a=$d

Це дозволить уникнути виконання частини після || випадково, коли код між && та || не вдається.


Це все одно не -e(set -o errexit; [ 5 == 5 ] && { false; true; echo "success"; } || echo "failure"; echo $?; echo "further on";)success 0 further on
вловить

2
Використовуйте :bulit-in замість trueзбереження-збереження execзовнішньої програми.
Том Хейл

@ivan_pozdeev Чи є спосіб скористатися &&.. ||і все-таки спіймати невдалу команду між ними?
Том Хейл

2
@TomHale No. &&і ||за визначенням застосовується до всієї команди перед ними. Отже, якщо перед вами є дві команди (незалежно від того, як вони поєднуються), ви не можете застосувати її лише до однієї з них, а не до іншої. Ви можете імітувати if/ then/ elseлогіку змінними прапора, але навіщо турбуватися, якщо є if/ then/ elseналежним?
ivan_pozdeev

14

Команда let підтримує більшість основних операторів, які знадобляться:

let a=b==5?c:d;

Природно, це працює лише для призначення змінних; він не може виконувати інші команди.


24
він точно еквівалентний ((...)), тому він дійсний лише для арифметичних виразів
drAlberT

13

Ось ще один варіант, коли вам потрібно лише один раз вказати змінну, яку ви присвоюєте, і не має значення, яка ваша присвоєння - це рядок чи число:

VARIABLE=`[ test ] && echo VALUE_A || echo VALUE_B`

Просто думка. :)


Основний мінус: він також охопить складність [ test ]. Таким чином, конструкція безпечна для використання, лише якщо ви "знаєте", що команда нічого не видає для stdout.
ivan_pozdeev

Також те саме, що stackoverflow.com/questions/3953645/ternary-operator-in-bash/… плюс необхідність цитувати та скасувати цитати всередині. Так виглядатиме термінова до простору версія VARIABLE="`[ test ] && echo \"VALUE_A\" || echo \"VALUE_B\"`".
ivan_pozdeev

8

Для моїх випадків використання, здається, працює:

Приклади

$ tern 1 YES NO                                                                             
YES

$ tern 0 YES NO                                                                             
NO

$ tern 52 YES NO                                                                            
YES

$ tern 52 YES NO 52                                                                         
NO

і може бути використаний у такому сценарії:

RESULT=$(tern 1 YES NO)
echo "The result is $RESULT"

терн

function show_help()
{
  echo ""
  echo "usage: BOOLEAN VALUE_IF_TRUE VALUE_IF_FALSE {FALSE_VALUE}"
  echo ""
  echo "e.g. "
  echo ""
  echo "tern 1 YES NO                            => YES"
  echo "tern 0 YES NO                            => NO"
  echo "tern "" YES NO                           => NO"
  echo "tern "ANY STRING THAT ISNT 1" YES NO     => NO"
  echo "ME=$(tern 0 YES NO)                      => ME contains NO"
  echo ""

  exit
}

if [ "$1" == "help" ]
then
  show_help
fi
if [ -z "$3" ]
then
  show_help
fi

# Set a default value for what is "false" -> 0
FALSE_VALUE=${4:-0}

function main
{
  if [ "$1" == "$FALSE_VALUE" ]; then
    echo $3
    exit;
  fi;

  echo $2
}

main "$1" "$2" "$3"

7
Дуже пояснювальна. Дуже повно. Дуже багатослівний. Три речі, які мені подобаються. ;-)
Джессі Чисгольм

2
є ternчастиною Bash?
Мабуть,

4
hey @ 太極 者 無極 而 生 - ось назва скрипту bash - збережіть цей розділ "tern" вище у файлі з назвою "tern", а потім запустіть chmod 700 ternу тій же папці. Тепер у вас буде ternкоманда у своєму терміналі
Brad Parks

8

У програмі Shell Scripting для потрійного оператора ми можемо використовувати наступні три способи:

    [ $numVar == numVal ] && resVar="Yop" || resVar="Nop"

Or

    resVar=$([ $numVar == numVal ] && echo "Yop" || echo "Nop")

Or

    (( numVar == numVal ? (resVar=1) : (resVar=0) ))

Це фактично (перший і другий конкретно з цих трьох прикладів) найбільш відповідна відповідь на це питання ІМХО.
netpoetica

6

У башті також є дуже подібний синтаксис для потрійних умовних умов:

a=$(( b == 5 ? 123 : 321  ))

На жаль, він працює лише з цифрами - наскільки я знаю.



4

Ви можете використовувати це, якщо хочете схожий синтаксис

a=$(( $((b==5)) ? c : d ))

Це працює лише з цілими числами. Ви можете записати його простіше як a=$(((b==5) ? : c : d))- $((...))потрібен лише тоді, коли ми хочемо віднести результат арифметики до іншої змінної.
codeforester

2

Ось кілька варіантів:

1- Використовуйте, якщо тоді ще в одному рядку, можливо.

if [[ "$2" == "raiz" ]] || [[ "$2" == '.' ]]; then pasta=''; else pasta="$2"; fi

2- Напишіть функцію, як це:

 # Once upon a time, there was an 'iif' function in MS VB ...

function iif(){
  # Echoes $2 if 1,banana,true,etc and $3 if false,null,0,''
  case $1 in ''|false|FALSE|null|NULL|0) echo $3;;*) echo $2;;esac
}

використовувати всередині сценарію, як це

result=`iif "$expr" 'yes' 'no'`

# or even interpolating:
result=`iif "$expr" "positive" "negative, because $1 is not true"` 

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

 case "$expr" in ''|false|FALSE|null|NULL|0) echo "no...$expr";;*) echo "yep $expr";;esac

 # Expression can be something like:     
   expr=`expr "$var1" '>' "$var2"`

2

Ось загальне рішення, це

  • також працює з рядковими тестами
  • відчуває радше як вираз
  • уникає будь-яких тонких побічних ефектів, коли стан провалюється

Тест з числовим порівнянням

a = $(if [ "$b" -eq 5 ]; then echo "$c"; else echo "$d"; fi)

Тест зі порівнянням рядків

a = $(if [ "$b" = "5" ]; then echo "$c"; else echo "$d"; fi)



1

відповісти на: int a = (b == 5) ? c : d;

просто напишіть:

b=5
c=1
d=2
let a="(b==5)?c:d"

echo $a # 1

b=6;
c=1;
d=2;
let a="(b==5)?c:d"

echo $a # 2

пам'ятайте, що "вираз" еквівалентний $ ((вираз))


0

Альтернативна орієнтація на рядок, яка використовує масив:

spec=(IGNORE REPLACE)
for p in {13..15}; do
  echo "$p: ${spec[p==14]}";
done

який виводить:

13: IGNORE
14: REPLACE
15: IGNORE
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.