Operador ternário (? :) no Bash

441

Existe uma maneira de fazer algo assim

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

usando o Bash?

En_t8
fonte
1
A resposta da @ dutCh mostra que bashtem algo semelhante ao "operador ternário", no entanto, bashisso é chamado de "operador condicional" expr?expr:expr(consulte a man bashseção "Avaliação Aritmética"). Lembre-se de que o bash"operador condicional" é complicado e possui algumas dicas.
Trevor Boyd Smith
O Bash possui um operador ternário para números inteiros e funciona dentro da expressão aritmética ((...)). Consulte Aritmética da casca .
codeforester 01/09/18
1
Assim como o @codeforester mencionou, o operador ternário trabalha com expansão $(( ))aritmética e avaliação aritmética (( )). Veja também https://mywiki.wooledge.org/ArithmeticExpression.
Kai

Respostas:

489

operador ternário ? :é apenas uma forma curta deif/else

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

Ou

 [[ $b = 5 ]] && a="$c" || a="$d"
ghostdog74
fonte
103
Observe que o =operador testa a igualdade das cadeias, não a igualdade numérica (ou seja, [[ 05 = 5 ]]é falsa). Se você quiser comparação numérica, use -eq.
Gordon Davisson
10
É mais de um pequeno formulário paraif/then/else
vol7ron
15
É uma maneira do gênio de utilizar o comportamento de curto-circuito para obter um efeito ternário operador :) :) :)
mtk
6
por que o [[e]]? funciona tão bem assim: [$ b = 5] && a = "$ c" || a = "$ d"
kdubs 29/10
83
A cond && op1 || op2construção possui um erro inerente: se op1tiver um status de saída diferente de zero por qualquer motivo, o resultado será silenciosamente op2. if cond; then op1; else op2; fié uma linha também e não tem esse defeito.
precisa saber é o seguinte
375

Código:

a=$([ "$b" == 5 ] && echo "$c" || echo "$d")
Vladimir
fonte
58
isso é melhor que os outros ... o ponto sobre o operador terciário é que ele é um operador; portanto, o contexto apropriado está em uma expressão; portanto, ele deve retornar um valor.
Ferrier nic
2
Esta é a maneira mais concisa. Esteja ciente de que se a parte com echo "$c"for um comando com alias e com várias linhas (como echo 1; echo 2), coloque-a entre parênteses.
Matt
2
Isso também capturará qualquer saída do comando testado (sim, neste caso em particular, "sabemos" que não produz nenhum).
precisa saber é o seguinte
8
Essa cadeia de operadores se comporta apenas como um operador ternário se você tiver certeza de que o comando depois &&não terá um status de saída diferente de zero. Caso contrário, a && b || c será executado "inesperadamente" cse afor bem-sucedido, mas bfalhar.
Chepner # 15/18
155

Se a condição estiver apenas verificando se uma variável está definida, há ainda uma forma mais curta:

a=${VAR:-20}

atribuirá ao valor de VARse VARestiver definido, caso contrário, atribuirá a ele o valor padrão20 - isso também pode ser resultado de uma expressão.

Como alex observa no comentário, essa abordagem é tecnicamente chamada de "Expansão de parâmetros".

nemesisfixx
fonte
6
No caso de passar um parâmetro de cadeia com hífens na mesma, eu tive que usar aspas: a=${1:-'my-hyphenated-text'}
saranicole
1
ligação para o preguiçoso - há operadores adicionais que apenas substituto ( :-)
Justin Wrobel
10
@ JustinWrobel - infelizmente nenhuma sintaxe gosta ${VAR:-yes:-no}.
Ken Williams
76
if [ "$b" -eq 5 ]; then a="$c"; else a="$d"; fi

A cond && op1 || op2expressão sugerida em outras respostas tem um erro inerente: se op1tiver um status de saída diferente de zero, op2silenciosamente se torna o resultado; o erro também não será detectado no -emodo. Portanto, essa expressão só é segura de usar se op1nunca pode falhar (por exemplo :,true se um builtin, ou atribuição de variável sem quaisquer operações que podem falhar (como divisão e chamadas OS)).

Observe as ""aspas. O primeiro par evitará um erro de sintaxe se $bestiver em branco ou tiver espaço em branco. Outros impedirão a conversão de todos os espaços em branco em espaços únicos.

ivan_pozdeev
fonte
1
Eu também vi prefixar, acredito que [ "x$b" -eq "x" ]costumava testar especificamente a string vazia. Eu gosto de usar as sintaxes usadas pelo zsh ('PARAMETER EXPANSION' na página de manual do zshexpn), mas não sei muito sobre sua portabilidade.
John P
46
(( a = b==5 ? c : d )) # string + numeric
holandês
fonte
31
Isso é bom para numéricos comparações e atribuições, mas dará resultados imprevisíveis se você usá-lo para comparações de strings e atribuições .... (( ))trata qualquer / todas as cordas como0
Peter.O
3
Isso também pode ser escrito:a=$(( b==5 ? c : d ))
joeytwiddle
33
[ $b == 5 ] && { a=$c; true; } || a=$d

Isso evitará executar a parte após || por acidente quando o código entre && e || falha.

Sir Athos
fonte
Isso ainda não detectará o erro no -emodo: (set -o errexit; [ 5 == 5 ] && { false; true; echo "success"; } || echo "failure"; echo $?; echo "further on";)->success 0 further on
ivan_pozdeev 01/04
2
Use a :inserção em vez de truesalvar execum programa externo.
Tom Hale
@ivan_pozdeev Existe alguma maneira de usar &&.. ||e ainda pegar um comando com falha entre eles?
Tom Hale
2
@TomHale No. &&e, ||por definição, aplicam-se a todo o comando antes deles. Portanto, se você tiver dois comandos antes dele (independentemente de como eles são combinados), não poderá aplicá-lo apenas a um deles e não ao outro. Você pode emular if/ then/ elselogic com variáveis ​​de flag, mas por que se preocupar se há if/ then/ elseadequado?
ivan_pozdeev
14

O comando let suporta a maioria dos operadores básicos necessários:

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

Naturalmente, isso funciona apenas para atribuir variáveis; não pode executar outros comandos.

emu
fonte
24
é exatamente equivalente a ((...)), por isso é válido apenas para expressões aritméticas
drAlberT
13

Aqui está outra opção em que você só precisa especificar a variável que está atribuindo uma vez e não importa se sua atribuição é uma sequência ou um número:

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

Apenas um pensamento. :)

Jasonovich
fonte
Uma grande desvantagem: ele também irá capturar o padrão de [ test ]. Portanto, a construção só é segura se você "souber" que o comando não gera nada para o stdout.
ivan_pozdeev
Também o mesmo que stackoverflow.com/questions/3953645/ternary-operator-in-bash/…, além da necessidade de citar e escapar de aspas dentro. A versão tolerante a espaço será semelhante VARIABLE="`[ test ] && echo \"VALUE_A\" || echo \"VALUE_B\"`".
ivan_pozdeev
8

O seguinte parece funcionar para meus casos de uso:

Exemplos

$ tern 1 YES NO                                                                             
YES

$ tern 0 YES NO                                                                             
NO

$ tern 52 YES NO                                                                            
YES

$ tern 52 YES NO 52                                                                         
NO

e pode ser usado em um script como este:

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

andorinha-do-mar

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"
Brad Parks
fonte
7
Muito explicativo. Muito completo. Muito detalhado. Eu gosto de três coisas. ;-)
Jesse Chisholm
2
faz ternparte do Bash? Mac parece não ter isso
polaridade
4
hey @ 太極 者 無極 而 生 - esse é o nome do script bash - salve a seção "tern" acima em um arquivo chamado "tern" e execute chmod 700 ternna mesma pasta. Agora você terá um terncomando no seu terminal
Brad Parks
8

Podemos usar as seguintes três maneiras no Shell Scripting para operador ternário:

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

Or

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

Or

    (( numVar == numVal ? (resVar=1) : (resVar=0) ))
Sujay UN
fonte
Esta é realmente (a primeira e a segunda especificamente, desses três exemplos) a resposta mais apropriada para esta pergunta IMHO.
Netpoetica 10/10
6

Há também uma sintaxe muito semelhante para condicionais ternários no bash:

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

Infelizmente, ele só funciona com números - até onde eu sei.

Andre Dias
fonte
5
(ping -c1 localhost&>/dev/null) && { echo "true"; } || {  echo "false"; }
wibble
fonte
4

Você pode usar isso se quiser uma sintaxe semelhante

a=$(( $((b==5)) ? c : d ))
PRIYESH PATEL
fonte
Isso funciona apenas com números inteiros. Você pode escrevê-lo de forma mais simples, pois a=$(((b==5) ? : c : d))- $((...))é necessário apenas quando queremos atribuir o resultado da aritmética a outra variável.
codeforester 01/09/19
2

Aqui estão algumas opções:

1- Use se, em seguida, em uma linha, é possível.

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

2- Escreva uma função como esta:

 # 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
}

use script interno como este

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

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

3- Inspirado na resposta do caso, um uso mais flexível e de uma linha é:

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

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

Aqui está uma solução geral, que

  • funciona com testes de string também
  • parece bastante uma expressão
  • evita efeitos colaterais sutis quando a condição falha

Teste com comparação numérica

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

Teste com comparação de cadeias

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

Stefan Haberl
fonte
1

Isso é muito parecido com a boa resposta de Vladimir . Se o seu "ternário" é um caso de "se verdadeiro, sequência, se falso, vazio", você pode simplesmente fazer:

$ c="it was five"
$ b=3
$ a="$([[ $b -eq 5 ]] && echo "$c")"
$ echo $a

$ b=5
$ a="$([[ $b -eq 5 ]] && echo "$c")"
$ echo $a
it was five
Bruno Bronosky
fonte
1

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

apenas escreva:

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

lembre-se de que "expressão" é equivalente a $ ((expressão))

André Hillaire
fonte
0

Uma alternativa orientada a cadeias, que usa uma matriz:

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

quais saídas:

13: IGNORE
14: REPLACE
15: IGNORE
druid62
fonte