Opérateur ternaire (? :) dans Bash

445

Y a-t-il un moyen de faire quelque chose comme ça

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

en utilisant Bash?

En_t8
la source
1
La réponse de @ dutCh montre qu'il basha quelque chose de similaire à "l'opérateur ternaire", mais dans bashce cas, il est appelé "opérateur conditionnel" expr?expr:expr(voir la man bashsection "Évaluation arithmétique"). Gardez à l'esprit que l ' bash"opérateur conditionnel" est délicat et comporte quelques accrochages.
Trevor Boyd Smith
Bash a un opérateur ternaire pour les entiers et il fonctionne à l'intérieur de l'expression arithmétique ((...)). Voir Shell Arithmetic .
codeforester
1
Tout comme @codeforester l'a mentionné, l'opérateur ternaire travaille avec l'expansion $(( ))arithmétique et l'évaluation arithmétique (( )). Voir aussi https://mywiki.wooledge.org/ArithmeticExpression.
Kai

Réponses:

490

opérateur ternaire ? :est juste une forme abrégée deif/else

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

Ou

 [[ $b = 5 ]] && a="$c" || a="$d"
ghostdog74
la source
103
Notez que l' =opérateur teste l'égalité des chaînes, pas l'égalité numérique (c'est [[ 05 = 5 ]]-à- dire est faux). Si vous souhaitez une comparaison numérique, utilisez -eqplutôt.
Gordon Davisson
10
C'est plus une forme courte pourif/then/else
vol7ron
15
C'est une manière géniale d'utiliser le comportement de court-circuit pour obtenir un effet d'opérateur ternaire :) :) :)
mtk
6
pourquoi le [[et]]? cela fonctionne aussi bien comme ceci: [$ b = 5] && a = "$ c" || a = "$ d"
kdubs
83
La cond && op1 || op2construction a un bug inhérent: si le op1statut de sortie est différent de zéro pour une raison quelconque, le résultat deviendra silencieusement op2. if cond; then op1; else op2; fiest aussi une ligne et n'a pas ce défaut.
ivan_pozdeev
375

Code:

a=$([ "$b" == 5 ] && echo "$c" || echo "$d")
Vladimir
la source
58
c'est mieux que les autres ... le point sur l'opérateur tertiaire est que c'est un opérateur, donc son contexte est dans une expression, donc il doit retourner une valeur.
nic ferrier
2
C'est la manière la plus concise. Sachez que si la pièce avec echo "$c"est une commande aliasée à plusieurs lignes (comme echo 1; echo 2), vous devez la mettre entre parenthèses.
Matt
2
Cela capturera également toute sortie de la commande testée (oui, dans ce cas particulier, nous "savons" qu'elle n'en produit aucune).
ivan_pozdeev
8
Cette chaîne d'opérateurs ne se comporte comme un opérateur ternaire que si vous êtes certain que la commande suivante &&n'aura pas un état de sortie non nul. Sinon, a && b || c s'exécutera "de manière inattendue" en ccas de asuccès mais d' béchec.
chepner
155

Si la condition vérifie simplement si une variable est définie, il existe même une forme plus courte:

a=${VAR:-20}

affectera à ala valeur de VARif VARest définie, sinon il lui attribuera la valeur par défaut 20- cela peut également être le résultat d'une expression.

Comme le note Alex dans le commentaire, cette approche est appelée techniquement "Expansion des paramètres".

nemesisfixx
la source
6
Dans le cas de passer un paramètre de chaîne avec des tirets, j'ai dû utiliser des guillemets: a=${1:-'my-hyphenated-text'}
saranicole
1
lien pour les paresseux - il y a d'autres opérateurs que juste remplacer ( :-)
Justin Wrobel
10
@JustinWrobel - malheureusement pas de syntaxe comme ${VAR:-yes:-no}.
Ken Williams
76
if [ "$b" -eq 5 ]; then a="$c"; else a="$d"; fi

L' cond && op1 || op2expression suggérée dans d'autres réponses a un bogue inhérent: si elle op1a un statut de sortie différent de zéro, op2devient silencieusement le résultat; l'erreur ne sera pas non plus détectée en -emode. Donc, cette expression n'est sûre que si elle op1ne peut jamais échouer (par exemple :,true si une commande interne, ou l' affectation des variables sans aucune opération qui peut échouer (comme la division et les appels OS)).

Notez les ""citations. La première paire empêchera une erreur de syntaxe si elle $best vide ou a des espaces. D'autres empêcheront la traduction de tous les espaces en espaces simples.

ivan_pozdeev
la source
1
J'ai également vu le préfixe, je crois [ "x$b" -eq "x" ]utilisé pour tester spécifiquement la chaîne vide. J'aime utiliser les syntaxes utilisées par zsh ('PARAMETER EXPANSION' dans la page de manuel zshexpn) mais je ne connais pas grand-chose à leur portabilité.
John P
46
(( a = b==5 ? c : d )) # string + numeric
néerlandais
la source
31
C'est bon pour les comparaisons et les affectations numériques , mais cela donnera des résultats imprévisibles si vous l'utilisez pour les comparaisons et les affectations de chaînes .... (( ))traite toutes les chaînes comme0
Peter.O
3
Cela peut aussi être écrit:a=$(( b==5 ? c : d ))
joeytwiddle
33
[ $b == 5 ] && { a=$c; true; } || a=$d

Cela évitera d'exécuter la pièce après || par accident lorsque le code entre && et || échoue.

Sir Athos
la source
Cela n'attrapera toujours pas l'erreur en -emode: (set -o errexit; [ 5 == 5 ] && { false; true; echo "success"; } || echo "failure"; echo $?; echo "further on";)->success 0 further on
ivan_pozdeev
2
Utilisez le :bulit-in au lieu d' trueenregistrer execun programme externe.
Tom Hale
@ivan_pozdeev Existe-t-il un moyen d'utiliser &&.. ||et d'attraper toujours une commande ayant échoué entre eux?
Tom Hale
2
@TomHale No. &&et, ||par définition, s'appliquent à la commande entière avant eux. Donc, si vous avez deux commandes devant lui (quelle que soit la façon dont elles sont combinées), vous ne pouvez pas l'appliquer uniquement à l'une d'entre elles et non à l'autre. Vous pouvez émuler if/ then/ elselogic avec des variables de drapeau, mais pourquoi s'embêter s'il y a if/ then/ elsebon?
ivan_pozdeev
14

La commande let prend en charge la plupart des opérateurs de base dont on aurait besoin:

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

Naturellement, cela ne fonctionne que pour affecter des variables; il ne peut pas exécuter d'autres commandes.

émeu
la source
24
il est exactement équivalent à ((...)), il n'est donc valable que pour les expressions arithmétiques
drAlberT
13

Voici une autre option où vous n'avez qu'à spécifier la variable que vous attribuez une fois, et peu importe si votre affectation est une chaîne ou un nombre:

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

Juste une pensée. :)

Jasonovich
la source
Un inconvénient majeur: il captera également la sortie standard de [ test ]. Ainsi, la construction n'est sûre que si vous "savez" que la commande ne renvoie rien à stdout.
ivan_pozdeev
Également le même que stackoverflow.com/questions/3953645/ternary-operator-in-bash/… plus la nécessité de citer et d'échapper les citations à l'intérieur. La version à tolérance d'espace ressemblera VARIABLE="`[ test ] && echo \"VALUE_A\" || echo \"VALUE_B\"`".
ivan_pozdeev
8

Les éléments suivants semblent fonctionner pour mes cas d'utilisation:

Exemples

$ tern 1 YES NO                                                                             
YES

$ tern 0 YES NO                                                                             
NO

$ tern 52 YES NO                                                                            
YES

$ tern 52 YES NO 52                                                                         
NO

et peut être utilisé dans un script comme celui-ci:

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

sterne

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
la source
7
Très explicatif. Très complet. Très verbeux. Trois choses que j'aime. ;-)
Jesse Chisholm
2
fait ternpartie de Bash? Mac ne semble pas l'avoir
non
4
hey @ 太極 者 無極 而 生 - c'est le nom du script bash - enregistrez la section "tern" ci-dessus dans un fichier nommé "tern", puis exécutez-le chmod 700 terndans le même dossier. Maintenant, vous aurez une terncommande dans votre terminal
Brad Parks
8

Nous pouvons utiliser les trois méthodes suivantes dans Shell Scripting pour l'opérateur ternaire:

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

Or

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

Or

    (( numVar == numVal ? (resVar=1) : (resVar=0) ))
Sujay UN
la source
C'est en fait (le premier et le deuxième en particulier, de ces trois exemples) la réponse la plus appropriée à cette question à mon humble avis.
netpoetica
6

Il existe également une syntaxe très similaire pour les conditions ternaires en bash:

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

Malheureusement, cela ne fonctionne qu'avec des chiffres - pour autant que je sache.

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

Vous pouvez l'utiliser si vous souhaitez une syntaxe similaire

a=$(( $((b==5)) ? c : d ))
PRIYESH PATEL
la source
Cela ne fonctionne qu'avec des entiers. Vous pouvez l'écrire plus simplement car a=$(((b==5) ? : c : d))- $((...))n'est nécessaire que lorsque nous voulons affecter le résultat de l'arithmétique à une autre variable.
codeforester
2

Voici quelques options:

1- Utiliser si alors autrement sur une seule ligne, c'est possible.

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

2- Écrivez une fonction comme celle-ci:

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

utiliser un script intérieur comme celui-ci

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

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

3- Inspiré de la réponse au cas, une utilisation plus flexible et sur une seule ligne est:

 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
la source
2

Voici une solution générale,

  • fonctionne également avec les tests de chaîne
  • ressemble plutôt à une expression
  • évite tous les effets secondaires subtils lorsque la condition échoue

Test avec comparaison numérique

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

Test avec comparaison de chaînes

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

Stefan Haberl
la source
2

répondre à: int a = (b == 5) ? c : d;

Ecrivez:

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

rappelez-vous que "expression" est équivalente à $ ((expression))

André Hillaire
la source
1

Cela ressemble beaucoup à la bonne réponse de Vladimir . Si votre "ternaire" est un cas de "si vrai, chaîne, si faux, vide", alors vous pouvez simplement faire:

$ 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
la source
0

Une alternative orientée chaîne, qui utilise un tableau:

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

qui génère:

13: IGNORE
14: REPLACE
15: IGNORE
druid62
la source