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.