Operatore ternario (? :) in Bash


445

C'è un modo per fare qualcosa del genere

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

usando Bash?


1
La risposta di @ dutCh mostra che bashha qualcosa di simile all '"operatore ternario", tuttavia in bashquesto viene chiamato "operatore condizionale" expr?expr:expr(vedi la man bashsezione "Vai alla valutazione aritmetica"). Tieni presente che l ' bash"operatore condizionale" è complicato e presenta alcuni aspetti positivi.
Trevor Boyd Smith,

Bash ha un operatore ternario per numeri interi e funziona all'interno dell'espressione aritmetica ((...)). Vedi Shell Arithmetic .
codeforester

1
Proprio come menzionato @codeforester, l'operatore ternario lavora con l'espansione $(( ))aritmetica e la valutazione aritmetica (( )). Vedi anche https://mywiki.wooledge.org/ArithmeticExpression.
Kai,

Risposte:


491

operatore ternario ? :è solo una forma abbreviata diif/else

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

O

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

103
Si noti che l' =operatore verifica l'uguaglianza delle stringhe, non l'uguaglianza numerica (ovvero [[ 05 = 5 ]]è falsa). Se si desidera un confronto numerico, utilizzare -eqinvece.
Gordon Davisson,

10
È più una forma abbreviata diif/then/else
vol7ron

15
È un modo geniale di utilizzare il comportamento del corto circuito per ottenere un effetto ternario sull'operatore :) :) :)
mtk,

6
perché il [[e]]? funziona altrettanto bene in questo modo: [$ b = 5] && a = "$ c" || a = "$ d"
kdubs il

83
Il cond && op1 || op2costrutto ha un bug intrinseco: se op1ha uno stato di uscita diverso da zero per qualsiasi motivo, il risultato diventerà silenziosamente op2. if cond; then op1; else op2; fiè anche una riga e non presenta questo difetto.
ivan_pozdeev,

375

Codice:

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

58
questo è meglio degli altri ... il punto sull'operatore terziario è che è un operatore, quindi il suo giusto contesto è in un'espressione, quindi deve restituire un valore.
Nic Ferrier

2
Questo è il modo più conciso. Tenere presente che se la parte con echo "$c"è un comando con alias, multilinea (come echo 1; echo 2), è necessario racchiuderla tra parentesi.
Matt,

2
Questo catturerà anche qualsiasi output del comando testato (sì, in questo caso particolare, "sappiamo" che non ne produce alcuno).
ivan_pozdeev,

8
Questa catena di operatori si comporta come un operatore ternario solo se si è certi che il comando dopo &&non avrà uno stato di uscita diverso da zero. Altrimenti, a && b || c verrà eseguito "inaspettatamente" cse ha esito apositivo ma bnon riesce.
chepner,

155

Se la condizione sta semplicemente verificando se è impostata una variabile, esiste anche una forma più breve:

a=${VAR:-20}

assegnerà al avalore di VARif VARè impostato, altrimenti assegnerà il valore predefinito 20- questo può anche essere il risultato di un'espressione.

Come osserva alex nel commento, questo approccio è tecnicamente chiamato "Espansione dei parametri".


6
Nel caso di passare un parametro stringa con trattini dentro, ho dovuto usare le virgolette: a=${1:-'my-hyphenated-text'}
saranicole

1
link per il pigro - ci sono operatori aggiuntivi oltre al semplice sostituto ( :-)
Justin Wrobel,

10
@JustinWrobel - purtroppo nessuna sintassi simile ${VAR:-yes:-no}.
Ken Williams,

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

L' cond && op1 || op2espressione suggerita in altre risposte ha un bug intrinseco: se op1ha uno stato di uscita diverso da zero, op2diventa silenziosamente il risultato; anche l'errore non verrà rilevato in -emodalità. Quindi, quell'espressione è sicura da usare solo se op1non può mai fallire (ad es. :, trueSe incorporata, o assegnazione di variabili senza operazioni che possono fallire (come le chiamate di divisione e OS)).

Nota le ""virgolette. La prima coppia eviterà un errore di sintassi se $bè vuota o ha spazi bianchi. Altri impediranno la traduzione di tutti gli spazi bianchi in singoli spazi.


1
Ho anche visto il prefisso, credo [ "x$b" -eq "x" ]utilizzato per testare specificamente la stringa vuota. Mi piace usare le sintassi usate da zsh ('PARAMETER EXPANSION' nella manpage zshexpn) ma non so molto sulla loro portabilità.
John P,


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

31
Questo è utile per confronti numerici e assegnazioni, ma darà risultati imprevedibili se lo si utilizza per confronti di stringhe e assegnazioni .... (( ))tratta qualsiasi / tutte le stringhe come0
Peter.O

3
Questo può anche essere scritto:a=$(( b==5 ? c : d ))
joeytwiddle il

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

Ciò eviterà di eseguire la parte dopo || per caso quando il codice tra && e || non riesce.


Questo non rileverà ancora l'errore in -emodalità: (set -o errexit; [ 5 == 5 ] && { false; true; echo "success"; } || echo "failure"; echo $?; echo "further on";)->success 0 further on
ivan_pozdeev

2
Utilizzare il :bulit-in anziché trueper salvare execun programma esterno.
Tom Hale,

@ivan_pozdeev C'è un modo per usare &&.. ||e intercettare ancora un comando fallito tra di loro?
Tom Hale,

2
@TomHale No. &&e ||per definizione si applicano all'intero comando che li precede. Quindi, se hai due comandi prima di esso (indipendentemente da come sono combinati), non puoi applicarlo solo a uno di essi e non all'altro. È possibile emulare if/ then/ elselogica con le variabili di bandiera, ma perché preoccuparsi se c'è if/ then/ elsecorretto?
Ivan_pozdeev,

14

Il comando let supporta la maggior parte degli operatori di base necessari:

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

Naturalmente, questo funziona solo per l'assegnazione di variabili; non può eseguire altri comandi.


24
è esattamente equivalente a ((...)), quindi è valido solo per le espressioni aritmetiche
drAlberT

13

Ecco un'altra opzione in cui devi solo specificare la variabile che stai assegnando una volta e non importa se la tua assegnazione è una stringa o un numero:

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

Solo un pensiero. :)


Un grande svantaggio: catturerà anche lo stdout di [ test ]. Quindi il costrutto è sicuro da usare solo se "sai" che il comando non genera nulla su stdout.
ivan_pozdeev,

Lo stesso di stackoverflow.com/questions/3953645/ternary-operator-in-bash/… oltre alla necessità di citare e sfuggire alle citazioni all'interno. La versione con tolleranza di spazio sarà simile VARIABLE="`[ test ] && echo \"VALUE_A\" || echo \"VALUE_B\"`".
ivan_pozdeev,

8

Quanto segue sembra funzionare per i miei casi d'uso:

Esempi

$ tern 1 YES NO                                                                             
YES

$ tern 0 YES NO                                                                             
NO

$ tern 52 YES NO                                                                            
YES

$ tern 52 YES NO 52                                                                         
NO

e può essere utilizzato in uno script in questo modo:

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

sterna

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
Molto esplicativo. Molto completo. Molto prolisso. Tre cose che mi piacciono ;-)
Jesse Chisholm

2
fa ternparte di Bash? Il Mac non sembra averlo
polarità il

4
hey @ 太極 者 無極 而 生 - questo è il nome dello script bash - salva quella sezione "sterna" sopra in un file chiamato "sterna", quindi esegui chmod 700 ternnella stessa cartella. Ora avrai un terncomando nel tuo terminale
Brad Parks, il

8

Possiamo usare i seguenti tre modi in Shell Scripting per l'operatore ternario:

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

Or

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

Or

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

Questa è in realtà (la prima e la seconda in particolare, di questi tre esempi) la risposta più appropriata a questa domanda IMHO.
netpoetica,

6

C'è anche una sintassi molto simile per i condizionali ternari in bash:

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

Sfortunatamente, funziona solo con i numeri - per quanto ne so.


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

4

Puoi usarlo se vuoi una sintassi simile

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

Funziona solo con numeri interi. Puoi scriverlo più semplicemente come a=$(((b==5) ? : c : d))- $((...))è necessario solo quando vogliamo assegnare il risultato dell'aritmetica a un'altra variabile.
codeforester

2

Ecco alcune opzioni:

1- Usa se poi altro in una riga, è possibile.

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

2- Scrivi una funzione come questa:

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

usa lo script interno in questo modo

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

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

3- Ispirato alla risposta del caso, un uso più flessibile e di una riga è:

 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

Ecco una soluzione generale, quella

  • funziona anche con i test delle stringhe
  • sembra piuttosto un'espressione
  • evita eventuali effetti collaterali sottili quando la condizione fallisce

Test con confronto numerico

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

Test con confronto delle stringhe

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


2

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

Scrivi e basta:

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

ricorda che "espressione" equivale a $ ((espressione))


1

È molto simile alla bella risposta di Vladimir . Se il tuo "ternario" è un caso di "se vero, stringa, se falso, vuoto", allora puoi semplicemente fare:

$ 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

0

Un'alternativa orientata alle stringhe, che utilizza un array:

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

che produce:

13: IGNORE
14: REPLACE
15: IGNORE
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.