751 Stimmen

Ternärer Operator (?:) in Bash

Gibt es eine Möglichkeit, so etwas zu tun

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

mit Bash?

1 Stimmen

@dutCh's Antwort zeigt, dass bash tatsächlich etwas Ähnliches wie den "ternären Operator" hat, jedoch wird dies in bash als "konditionaler Operator" expr?expr:expr bezeichnet (siehe man bash Abschnitt "Arithmetische Auswertung"). Beachten Sie, dass der bash "konditionale Operator" knifflig ist und einige Fallstricke hat.

0 Stimmen

Bash verfügt über einen ternären Operator für ganze Zahlen und er funktioniert innerhalb des arithmetischen Ausdrucks ((...)). Siehe Shell-Arithmetik.

2 Stimmen

Genau wie @codeforester erwähnt hat, funktioniert der ternäre Operator mit arithmetischer Expansion $(( )) und arithmetischer Auswertung (( )). Siehe auch https://mywiki.wooledge.org/ArithmeticExpression.

0voto

Dan Bray Punkte 6353

Die Top-Antwort [[ $b = 5 ]] && a="$c" || a="$d" sollte nur verwendet werden, wenn Sie sicher sind, dass nach dem && kein Fehler auftritt, da sonst der Teil nach dem || fälschlicherweise ausgeführt wird.

Um das Problem zu lösen, habe ich eine Ternärfunktion geschrieben, die sich so verhält, wie sie sollte, und sogar die ? und : Operatoren verwendet:

Bearbeiten - neue Lösung

Hier ist meine neue Lösung, die weder $IFS noch ev(a/i)l verwendet.

function executeCmds()
{
    declare s s1 s2 i j k
    declare -A cmdTeile
    declare pIFS=$IFS
    IFS=$'\n'
    declare results=($(echo "$1" | grep -oP '{ .*? }'))
    IFS=$pIFS
    s="$1"
    for ((i=0; i < ${#results[@]}; i++)); do
        s="${s/${results[$i]}/'\0'}"
        results[$i]="${results[$i]:2:${#results[$i]}-3}"
        results[$i]=$(echo ${results[$i]%%";"*})
    done
    s="$s&&"
    let cmdTeile[t]=0
    while :; do
        i=${cmdTeile[t]}
        let cmdTeile[$i,t]=0
        s1="${s%%"&&"*}||"
        while :; do
            j=${cmdTeile[$i,t]}
            let cmdTeile[$i,$j,t]=0
            s2="${s1%%"||"*};"
            while :; do
                cmdTeile[$i,$j,${cmdTeile[$i,$j,t]}]=$(echo ${s2%%";"*})
                s2=${s2#*";"}
                let cmdTeile[$i,$j,t]++
                [[ $s2 ]] && continue
                break
            done
            s1=${s1#*"||"}
            let cmdTeile[$i,t]++
            [[ $s1 ]] && continue
            break
        done
        let cmdTeile[t]++
        s=${s#*"&&"}
        [[ $s ]] && continue
        break
    done
    declare lastError=0
    declare skipNext=false
    for ((i=0; i < ${cmdTeile[t]}; i++ )) ; do
        let j=0
        while :; do
            let k=0
            while :; do
                if $skipNext; then
                    skipNext=false
                else
                    if [[ "${cmdTeile[$i,$j,$k]}" == "\0" ]]; then
                         executeCmds "${results[0]}" && lastError=0 || lastError=1
                         results=("${results[@]:1}")
                    elif [[ "${cmdTeile[$i,$j,$k]:0:1}" == "!" || "${cmdTeile[$i,$j,$k]:0:1}" == "-" ]]; then
                        [ ${cmdTeile[$i,$j,$k]} ] && lastError=0 || lastError=1
                    else
                        ${cmdTeile[$i,$j,$k]}
                        lastError=$?
                    fi
                    if (( k+1 < cmdTeile[$i,$j,t] )); then
                        skipNext=false
                    elif (( j+1 < cmdTeile[$i,t] )); then
                        (( lastError==0 )) && skipNext=true || skipNext=false
                    elif (( i+1 < cmdTeile[t] )); then
                        (( lastError==0 )) && skipNext=false || skipNext=true
                    fi
                fi
                let k++
                [[ $k<${cmdTeile[$i,$j,t]} ]] || break
            done
            let j++
            [[ $j<${cmdTeile[$i,t]} ]] || break
        done
    done
    return $lastError
}

function t()
{
    declare commands="$@"
    find="$(echo ?)"
    replace='?'
    commands="${commands/$find/$replace}"
    readarray -d '?' -t statement <<< "$commands"
    condition=${statement[0]}
    readarray -d ':' -t statement <<< "${statement[1]}"
    success="${statement[0]}"
    failure="${statement[1]}"
    executeCmds "$condition" || { executeCmds "$failure"; return; }
    executeCmds "$success"
}

executeCmds trennt jede einzelne Anweisung voneinander ab, abgesehen von denen, die aufgrund der && und || Operatoren übersprungen werden sollen. Es verwendet [], wenn eine Anweisung mit ! oder einer Flagge beginnt.

Es gibt zwei Möglichkeiten, Anweisungen zu übergeben:

  1. Geben Sie die einzelnen Anweisungen unangegeben an, aber achten Sie darauf, die ;, && und || Operatoren zu zitieren.

    t ls / ? ls qqq '||' echo aaa : echo bbb '&&' ls qq

  2. Geben Sie alle Anweisungen zitiert an:

    t 'ls /a ? ls qqq || echo aaa : echo bbb && ls qq'

NB Ich fand keine Möglichkeit, die Parameter unangegeben an && und || Operatoren zu übergeben, da sie illegale Zeichen für Funktionsnamen und Aliase sind, und ich fand keine Möglichkeit, die Bash-Operatoren zu überschreiben.

Alte Lösung - verwendet ev(a/i)l

function t()
{
    pIFS=$IFS
    IFS="?"
    read condition success <<< "$@"
    IFS=":"
    read success failure <<< "$success"
    IFS=$pIFS
    eval "$condition" || { eval "$failure" ; return; }
    eval "$success"
}
t ls / ? ls qqq '||' echo aaa : echo bbb '&&' ls qq
t 'ls /a ? ls qqq || echo aaa : echo bbb && ls qq'

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X