[[y equivalencia de mayúsculas y minúsculas en bash

13

Hace

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

siempre se comportan igual que

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

o hay alguna trampa?

PSkocik
fuente
1
No puedo encontrar ningún caso en el que difieran, independientemente de la shoptconfiguración y los valores en $1o pattern, ni $?posteriormente. La única diferencia es que $1no se expande en la salida cuando se ejecuta bajo xtrace.
Kusalananda

Respuestas:

7

Sí, son (casi) completamente equivalentes.


Detalle

Dentro de una [ … ]construcción:

El =operador (o incluso la opción no posix ==) prueba la coincidencia de cadenas, no la coincidencia de patrones.

Dentro de una [[ ]]construcción (de man bash):

Cuando se utilizan los operadores == y! =, La cadena a la derecha del operador se considera un patrón y se corresponde de acuerdo con las reglas que se describen a continuación en Coincidencia de patrones . Si la opción de shell nocasematch está habilitada, la coincidencia se realiza sin tener en cuenta el caso de los caracteres alfabéticos. El valor de retorno es 0 si la cadena coincide (==) o no coincide (! =) Con el patrón, y 1 en caso contrario. Se puede citar cualquier parte del patrón para forzarlo a que coincida como una cadena.

Dentro de una caseconstrucción (de man bash, editado y énfasis mío):

palabra del caso en la lista [[(] patrón [| patrón] ...); ] ... esac
... intenta hacer coincidir cada patrón a su vez, utilizando las mismas reglas de coincidencia que para la expansión del nombre de ruta (ver Expansión del nombre de ruta a continuación). ... Cada patrón examinado se expande usando la expansión de tilde, la expansión de parámetros y variables, la sustitución aritmética, la sustitución de comandos y la sustitución de procesos. Si la opción de shell nocasematch está habilitada, la coincidencia se realiza sin tener en cuenta el caso de los caracteres alfabéticos.

Ambos Pattern Matchingy Pathname Expansionse usan para significar lo mismo dentro del manual de bash.

La única diferencia que puedo ver en el manual es:

`[[ … ]]`                                   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

Eso quote removalno se enumera explícitamente para la construcción del caso.
Lo que funciona para coincidir exactamente con esto (para el [[ … ]]):

Se puede citar cualquier parte del patrón para forzarlo a que coincida como una cadena.

Use esto para probar este último punto (ahora la variable no es un patrón):

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

¿Por qué casi?

  1. Implícito extglob:

    Desde la versión 4.3 de bash

    Cuando se utilizan los operadores '==' y '! =', La cadena a la derecha del operador se considera un patrón y coincide de acuerdo con las reglas que se describen a continuación en Coincidencia de patrones, como si la opción de shell extglob estuviera habilitada .

    Eso significa que un patrón utilizado con la opción extglob unset funcionará de manera diferente en una declaración de caso y dentro de una [[construcción después de bash versión 4.3.

  2. Implícito |:

    La sintaxis para case es:

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

    Lo que significa que puede haber varios patrones separados por un |(OR).

    Me gusta esto:

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

    Que coincidirá con una cadena de solo números o solo letras en abcde, como 1234o aabee, pero no 12ao b23.

    A [[funcionará de manera equivalente si se utilizan expresiones regulares (ver var p3):

    #!/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

fuente