Тернарный оператор (? :) в Баш


445

Есть ли способ сделать что-то подобное

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

используя Bash?


1
Ответ @ dutCh показывает, что у bashнего есть нечто похожее на «троичный оператор», однако bashэто называется «условный оператор» expr?expr:expr(см. man bashраздел «Арифметическая оценка»). Имейте в виду, что bash«условный оператор» хитрый и имеет некоторые ошибки.
Тревор Бойд Смит

У Bash есть троичный оператор для целых чисел, и он работает внутри арифметического выражения ((...)). Смотрите Арифметику Shell .
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 ]]Е. False). Если вы хотите числовое сравнение, используйте -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
это лучше, чем другие ... Суть третичного оператора в том, что это оператор, поэтому его надлежащий контекст находится в выражении, поэтому он должен возвращать значение.
Ник Ферри

2
Это самый лаконичный способ. Помните, что если часть с echo "$c"является командой с псевдонимами, состоящей из нескольких строк (например echo 1; echo 2), вы должны заключить ее в скобки.
Мэтт

2
Это также будет захватывать любые выходные данные протестированной команды (да, в данном конкретном случае мы «знаем», что она не выдает).
ivan_pozdeev

8
Эта цепочка операторов ведет себя только как троичный оператор, если вы уверены, что команда после &&не будет иметь ненулевой статус выхода. В противном случае a && b || c «неожиданно» запустится, cесли aполучится, но bне получится .
chepner

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
Используйте встроенную функцию :вместо trueсохранения execвнешней программы.
Том Хейл

@ivan_pozdeev Есть ли способ использовать &&.. ||и все же поймать неудачную команду между ними?
Том Хейл

2
@ TomHale Нет. &&И ||по определению относится ко всей команде перед ними. Таким образом, если перед вами две команды (независимо от того, как они объединены), вы не можете применить их только к одной из них, а не к другой. Вы можете эмулировать 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 ]. Таким образом, конструкция безопасна только в том случае, если вы «знаете», что команда ничего не выводит в стандартный вывод.
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
эй @ 太極 者 無極 而 生 - это имя скрипта bash - сохраните этот раздел «tern» выше в файл с именем «tern», а затем запустите chmod 700 ternв той же папке. Теперь у вас будет ternкоманда в вашем терминале
Брэд Паркс

8

Мы можем использовать следующие три способа в сценариях оболочки для троичного оператора:

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

Or

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

Or

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

Это на самом деле (первый и второй конкретно из этих трех примеров) наиболее подходящий ответ на этот вопрос ИМХО.
netpoetica

6

Существует также очень похожий синтаксис для троичных условных выражений в bash:

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)


2

ответить на: 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.