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


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.