Ternärer Operator (? :) in Bash

441

Gibt es eine Möglichkeit, so etwas zu tun?

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

mit Bash?

En_t8
quelle
1
Die Antwort von @ dutCh zeigt, dass bashes etwas Ähnliches wie den "ternären Operator" gibt, bashdies wird jedoch als "bedingter Operator" bezeichnet expr?expr:expr(siehe man bashAbschnitt "Arithmetische Auswertung"). Denken Sie daran, dass der bash"bedingte Operator" schwierig ist und einige Fallstricke hat.
Trevor Boyd Smith
Bash hat einen ternären Operator für ganze Zahlen und arbeitet innerhalb des arithmetischen Ausdrucks ((...)). Siehe Shell-Arithmetik .
Codeforester
1
Wie bereits bei @codeforester erwähnt, arbeitet der ternäre Operator mit arithmetischer Erweiterung $(( ))und arithmethischer Auswertung (( )). Siehe auch https://mywiki.wooledge.org/ArithmeticExpression.
Kai

Antworten:

489

ternärer Operator ? :ist nur eine Kurzform vonif/else

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

Oder

 [[ $b = 5 ]] && a="$c" || a="$d"
Ghostdog74
quelle
103
Beachten Sie, dass der =Operator auf Zeichenfolgengleichheit und nicht auf numerische Gleichheit prüft (dh [[ 05 = 5 ]]falsch ist). Wenn Sie einen numerischen Vergleich wünschen, verwenden Sie -eqstattdessen.
Gordon Davisson
10
Es ist eher eine Kurzform fürif/then/else
Vol7ron
15
Es ist ein genialer Weg, das Kurzschlussverhalten zu nutzen, um einen ternären Operatoreffekt zu erzielen :) :) :)
mtk
6
warum die [[und]]? es funktioniert genauso gut wie folgt: [$ b = 5] && a = "$ c" || a = "$ d"
kdubs
83
Das cond && op1 || op2Konstrukt hat einen inhärenten Fehler: Wenn op1der Exit-Status ungleich Null aus irgendeinem Grund vorliegt, wird das Ergebnis stillschweigend angezeigt op2. if cond; then op1; else op2; fiist auch eine Zeile und hat diesen Defekt nicht.
ivan_pozdeev
375

Code:

a=$([ "$b" == 5 ] && echo "$c" || echo "$d")
Vladimir
quelle
58
Dies ist besser als die anderen ... Der Punkt über den tertiären Operator ist, dass es sich um einen Operator handelt, daher befindet sich der richtige Kontext in einem Ausdruck, daher muss ein Wert zurückgegeben werden.
Nic Ferrier
2
Dies ist der prägnanteste Weg. echo "$c"Beachten Sie, dass Sie das Teil mit einem Alias ​​mit mehreren Zeilen (wie echo 1; echo 2) in Klammern setzen sollten.
Matt
2
Dies erfasst auch alle Ausgaben des getesteten Befehls (ja, in diesem speziellen Fall "wissen" wir, dass er keine erzeugt).
ivan_pozdeev
8
Diese Kette von Operatoren verhält sich nur wie ein ternärer Operator , wenn Sie sicher sind , dass der Befehl nach &&nicht einen von Null verschiedenen Exit - Status hat. Andernfalls a && b || c wird "unerwartet" ausgeführt, cwenn dies aerfolgreich ist, aber bfehlschlägt.
Chepper
155

Wenn die Bedingung lediglich prüft, ob eine Variable gesetzt ist, gibt es noch eine kürzere Form:

a=${VAR:-20}

wird adem Wert von VARif VARzugewiesen, andernfalls wird ihm der Standardwert zugewiesen 20- dies kann auch ein Ergebnis eines Ausdrucks sein.

Wie Alex im Kommentar bemerkt, wird dieser Ansatz technisch als "Parametererweiterung" bezeichnet.

nemesisfixx
quelle
6
Im Fall der Übergabe eines String-Parameters mit Bindestrichen musste ich Anführungszeichen verwenden: a=${1:-'my-hyphenated-text'}
Saranicole
1
Link für die Faulen - es gibt zusätzliche Operatoren als nur Ersatz ( :-)
Justin Wrobel
10
@ JustinWrobel - leider keine Syntax wie ${VAR:-yes:-no}.
Ken Williams
76
if [ "$b" -eq 5 ]; then a="$c"; else a="$d"; fi

Der cond && op1 || op2in anderen Antworten vorgeschlagene Ausdruck weist einen inhärenten Fehler auf: Wenn op1der Exit-Status ungleich Null ist,op2 stillschweigend zum Ergebnis. Der Fehler wird auch nicht im -eModus abgefangen . So ist dieser Ausdruck nur dann sicher , wenn zu verwenden , op1kann nie versagen (zB :, truewenn eine eingebaute oder variable Zuordnung ohne Operationen , die (wie Teilung und OS Anrufe ausfallen können)).

Beachten Sie die ""Anführungszeichen. Das erste Paar verhindert einen Syntaxfehler, wenn $bes leer ist oder Leerzeichen enthält. Andere verhindern die Übersetzung aller Leerzeichen in einzelne Leerzeichen.

ivan_pozdeev
quelle
1
Ich habe auch Präfixe gesehen, von denen ich glaube, dass [ "x$b" -eq "x" ]sie speziell für die leere Zeichenfolge getestet wurden. Ich verwende gerne die von zsh verwendeten Syntaxen ('PARAMETER EXPANSION' in der zshexpn-Manpage), aber ich weiß nicht viel über ihre Portabilität.
John P
46
(( a = b==5 ? c : d )) # string + numeric
Niederländisch
quelle
31
Dies ist gut für numerische Vergleiche und Zuweisungen, aber es wird unvorhersehbare Ergebnisse liefern, wenn Sie es für Zeichenfolgenvergleiche und Zuweisungen verwenden .... (( ))behandelt alle / alle Zeichenfolgen als0
Peter.O
3
Dies kann auch geschrieben werden:a=$(( b==5 ? c : d ))
joeytwiddle
33
[ $b == 5 ] && { a=$c; true; } || a=$d

Dadurch wird vermieden, dass das Teil nach || ausgeführt wird aus Versehen, wenn der Code zwischen && und || schlägt fehl.

Sir Athos
quelle
Dies wird den Fehler im -eModus immer noch nicht abfangen : (set -o errexit; [ 5 == 5 ] && { false; true; echo "success"; } || echo "failure"; echo $?; echo "further on";)->success 0 further on
ivan_pozdeev
2
Verwenden Sie das :Bulit-In, anstatt ein externes Programm truezu speichern exec.
Tom Hale
@ivan_pozdeev Gibt es eine Möglichkeit, &&.. zu verwenden ||und trotzdem einen fehlgeschlagenen Befehl zwischen ihnen abzufangen ?
Tom Hale
2
@ TomHale Nr. &&Und ||gelten per Definition für den gesamten Befehl vor ihnen. Wenn Sie also zwei Befehle vor sich haben (unabhängig davon, wie sie kombiniert werden), können Sie sie nicht nur auf einen von ihnen und nicht auf den anderen anwenden. Sie können emulieren if/ then/ elseLogik mit Flag - Variablen, aber warum die Mühe , wenn es if/ then/ elserichtige?
ivan_pozdeev
14

Der Befehl let unterstützt die meisten grundlegenden Operatoren, die benötigt werden:

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

Dies funktioniert natürlich nur zum Zuweisen von Variablen. Es können keine anderen Befehle ausgeführt werden.

Emu
quelle
24
es ist genau gleichbedeutend mit ((...)), daher gilt es nur für arithmetische Ausdrücke
drAlberT
13

Hier ist eine weitere Option, bei der Sie die Variable, die Sie zuweisen, nur einmal angeben müssen. Dabei spielt es keine Rolle, ob es sich bei Ihrer Zuweisung um eine Zeichenfolge oder eine Zahl handelt:

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

Nur ein Gedanke. :) :)

Jasonovich
quelle
Ein großer Nachteil: Es wird auch den Standard von erfassen [ test ]. Daher ist die Verwendung des Konstrukts nur dann sicher, wenn Sie "wissen", dass der Befehl nichts an stdout ausgibt.
ivan_pozdeev
Dies gilt auch für stackoverflow.com/questions/3953645/ternary-operator-in-bash/… sowie für die Notwendigkeit, Anführungszeichen im Inneren zu zitieren und zu umgehen . Die platztolerante Version wird so aussehen VARIABLE="`[ test ] && echo \"VALUE_A\" || echo \"VALUE_B\"`".
ivan_pozdeev
8

Folgendes scheint für meine Anwendungsfälle zu funktionieren:

Beispiele

$ tern 1 YES NO                                                                             
YES

$ tern 0 YES NO                                                                             
NO

$ tern 52 YES NO                                                                            
YES

$ tern 52 YES NO 52                                                                         
NO

und kann in einem Skript wie folgt verwendet werden:

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

Seeschwalbe

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
quelle
7
Sehr erklärend. Sehr vollständig. Sehr ausführlich. Drei Dinge, die ich mag. ;-)
Jesse Chisholm
2
ist ein ternTeil von Bash? Mac scheint es nicht zu haben
Unpolarität
4
hey @ 太極 者 無極 而 生 - das ist der Name des Bash-Skripts - speichern Sie den obigen Abschnitt "Seeschwalbe" in einer Datei mit dem Namen "Seeschwalbe" und führen Sie ihn dann chmod 700 ternim selben Ordner aus. Jetzt haben Sie einen ternBefehl in Ihrem Terminal
Brad Parks
8

In Shell Scripting können wir für ternäre Operatoren die folgenden drei Möglichkeiten verwenden:

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

Or

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

Or

    (( numVar == numVal ? (resVar=1) : (resVar=0) ))
Sujay UN
quelle
Dies ist tatsächlich (das erste und das zweite dieser drei Beispiele) meiner Meinung nach die am besten geeignete Antwort auf diese Frage.
Netpoetica
6

Es gibt auch eine sehr ähnliche Syntax für ternäre Bedingungen in bash:

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

Leider funktioniert es nur mit Zahlen - soweit ich weiß.

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

Sie können dies verwenden, wenn Sie eine ähnliche Syntax wünschen

a=$(( $((b==5)) ? c : d ))
PRIYESH PATEL
quelle
Dies funktioniert nur mit ganzen Zahlen. Sie können es einfacher schreiben als a=$(((b==5) ? : c : d))- $((...))wird nur benötigt, wenn wir das Ergebnis der Arithmetik einer anderen Variablen zuweisen möchten.
Codeforester
2

Hier sind einige Optionen:

1- Verwenden Sie, wenn dann sonst in einer Zeile, ist es möglich.

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

2- Schreiben Sie eine Funktion wie folgt:

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

Verwenden Sie ein solches Skript

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

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

3- Inspiriert von der Fallantwort ist eine flexiblere und einzeiligere Verwendung:

 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
quelle
2

Hier ist eine allgemeine Lösung

  • funktioniert auch mit Stringtests
  • fühlt sich eher wie ein Ausdruck an
  • vermeidet subtile Nebenwirkungen, wenn die Bedingung fehlschlägt

Test mit numerischem Vergleich

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

Test mit String-Vergleich

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

Stefan Haberl
quelle
1

Dies ähnelt Wladimir's guter Antwort . Wenn Ihr "ternäres" ein Fall von "wenn wahr, Zeichenfolge, wenn falsch, leer" ist, können Sie einfach Folgendes tun:

$ 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
quelle
1

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

einfach schreiben:

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

Denken Sie daran, dass "Ausdruck" $ ((Ausdruck)) entspricht.

André Hillaire
quelle
0

Eine stringorientierte Alternative, die ein Array verwendet:

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

welche Ausgänge:

13: IGNORE
14: REPLACE
15: IGNORE
druid62
quelle