[[e equivalência de maiúsculas e minúsculas no bash

12

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

sempre se comporta da mesma forma que

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

ou há alguma pegadinha?

    
por PSkocik 08.07.2016 / 12:35

1 resposta

7

Sim, eles são (quase) completamente equivalentes.

Detalhe

Dentro de uma construção [ … ] :

O operador = (ou mesmo a opção não posix de == ) testa a correspondência de strings, não a correspondência de padrões.

Dentro de um [[ ]] construct (do homem bash):

When the == and != operators are used, the string to the right of the operator is considered a pattern and matched according to the rules described below under Pattern Matching. If the shell option nocasematch is enabled, the match is performed without regard to the case of alphabetic characters. The return value is 0 if the string matches (==) or does not match (!=) the pattern, and 1 otherwise. Any part of the pattern may be quoted to force it to be matched as a string.

Dentro de um case construct (do man bash, editado e ênfase meu):

case word in [ [(] pattern [ | pattern ] ... ) list ;; ] ... esac
... tries to match it against each pattern in turn, using the same matching rules as for pathname expansion (see Pathname Expansion below). … Each pattern examined is expanded using tilde expansion, parameter and variable expansion, arithmetic substitution, command substitution, and process substitution. If the shell option nocasematch is enabled, the match is performed without regard to the case of alphabetic characters.

Tanto Pattern Matching como Pathname Expansion são usados para indicar o mesmo dentro do manual bash.

A única diferença que posso ver no manual é:

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

Esse quote removal não está explicitamente listado para a construção do caso.
Que funciona para corresponder exatamente isso (para o [[ … ]] ):

Any part of the pattern may be quoted to force it to be matched as a string.

Use isto para testar este último ponto (agora a variável é não um padrão):

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

Por que quase?

  1. extglob implícito:

    Desde versão 4.3 do bash

    When the ‘==’ and ‘!=’ operators are used, the string to the right of the operator is considered a pattern and matched according to the rules described below in Pattern Matching, as if the extglob shell option were enabled.

    Isso significa que um padrão usado com a opção extglob não definida funcionará de maneira diferente em uma instrução case e dentro de uma construção [[ após a versão 4.3 do bash.

  2. | implícito:

    A sintaxe do caso é:

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

    O que significa que pode haver vários padrões separados por | (OR).

    Assim:

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

    Que corresponderá a uma sequência de apenas números ou apenas letras em abcde , como 1234 ou aabee , mas não 12a ou b23 .

    Um [[ funcionará de maneira equivalente se regex (veja em var p3) forem usados:

    #!/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
    
por 08.07.2016 / 22:33