[[und Falläquivalenz in Bash

13

Tut

if [[ "$1" = pattern ]]; then
    hook
fi

benimm dich immer so wie

case "$1" in
    pattern) hook;;
esac

Oder gibt es irgendwelche Fallstricke?

PSkocik
quelle
1
Ich kann keine Fälle finden, in denen sie sich unterscheiden, unabhängig von shoptEinstellungen und Werten in $1oder patternoder $?danach. Der einzige Unterschied besteht darin, dass $1die Ausgabe beim Ausführen unter nicht erweitert wird xtrace.
Kusalananda

Antworten:

7

Ja, sie sind (fast) gleichwertig.


Detail

In einem [ … ]Konstrukt:

Der =Operator (oder auch die Nicht-Posix-Option von ==) testet den String-Abgleich, nicht den Muster-Abgleich.

Innerhalb eines [[ ]]Konstrukts (von man bash):

Wenn die Operatoren == und! = Verwendet werden, wird die Zeichenfolge rechts vom Operator als Muster betrachtet und gemäß den im Folgenden unter Mustervergleich beschriebenen Regeln abgeglichen . Wenn die Shell- Option "nocasematch" aktiviert ist, wird die Übereinstimmung ohne Berücksichtigung der Buchstaben ausgeführt. Der Rückgabewert ist 0, wenn der String mit dem Muster übereinstimmt (==) oder nicht übereinstimmt (! =), Andernfalls 1. Jeder Teil des Musters kann in Anführungszeichen gesetzt werden, um die Übereinstimmung als Zeichenfolge zu erzwingen.

Innerhalb eines caseKonstrukts (von man bash, bearbeitet und Hervorhebung von mir):

case word in [[(] pattern [| pattern] ...) list ;; ] ... esac
... versucht es gegen jedes Muster wiederum zu entsprechen, die gleiche Matching Regeln wie für Pfadnamenerweiterung (siehe Pathname Expansion weiter unten). … Jedes untersuchte Muster wird durch Tildeerweiterung, Parameter- und Variablenerweiterung, arithmetische Substitution, Befehlssubstitution und Prozesssubstitution erweitert. Wenn die Shell- Option "nocasematch" aktiviert ist, wird die Übereinstimmung ohne Berücksichtigung der Buchstaben ausgeführt.

Beide Pattern Matchingund Pathname Expansionwerden im Bash-Handbuch verwendet, um dasselbe zu bedeuten.

Der einzige Unterschied, den ich im Handbuch sehen kann, ist:

`[[ … ]]`                                   case
tilde  expansion                            tilde expansion
parameter and variable expansion            parameter and variable expansion
arithmetic expansion                        arithmetic substitution
command substitution                        command substitution
process substitution                        process substitution
quote removal

Dies quote removalist für das case-Konstrukt nicht explizit aufgeführt.
Was genau dazu passt (für die [[ … ]]):

Jeder Teil des Musters kann in Anführungszeichen gesetzt werden, um die Übereinstimmung als Zeichenfolge zu erzwingen.

Verwenden Sie dies, um diesen letzten Punkt zu testen (jetzt ist die Variable kein Muster):

case "$1" in
  "$pattern") echo case match
esac

Warum fast?

  1. Implizit extglob:

    Seit Version 4.3 von Bash

    Wenn die Operatoren '==' und '! =' Verwendet werden, wird die Zeichenfolge rechts neben dem Operator als Muster betrachtet und gemäß den im Abschnitt Mustervergleich beschriebenen Regeln abgeglichen, als ob die Option extglob shell aktiviert wäre .

    Das bedeutet, dass ein mit der Option extglob unset verwendetes Muster in einer case-Anweisung und in einem [[Konstrukt nach der bash-Version 4.3 anders funktioniert .

  2. Implizit |:

    Die Syntax für case lautet:

    case word in [ [(] pattern [ | pattern ] ... ) list ;; ] ... esac

    Dies bedeutet, dass mehrere Muster durch ein |(ODER) getrennt sein können.

    So was:

    shopt -s extglob;      p1="+([0-9])";       p2="+([abcde])"
    
    case "$1" in
        $p1|$p2)    echo "or case match" ; ;;
    esac

    Womit entweder nur eine Folge von Zahlen oder nur Buchstaben in abcde, wie 1234oder aabee, aber nicht 12aoder übereinstimmen b23.

    A [[wird gleichwertig funktionieren, wenn Regex (siehe var p3) verwendet wird:

    #!/bin/bash
    
    shopt -s extglob           ### Use extended globbing.
    shopt -s globasciiranges   ### The range [a-z] will expand to [abcdefghijklmnopqrstuvwxyz].
    
    pattern="+([0-9])"
    p1="+([0-9])"
    p2="+([a-z])"
    p3="^([0-9]+|[a-z]+)$"
    
    case "$1" in
        $pattern)   echo case1 match ; ;&
        $p1|$p2)    echo case2 match ; ;;
    esac
    
    [[ "$1" == $pattern ]] && echo if1 match
    [[ "$1" =~ $p3 ]] && echo if2 match

quelle