[[e equivalência de caso no bash

13

Faz

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

sempre se comporte da mesma maneira que

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

ou há alguma dica?

PSkocik
fonte
1
Não consigo encontrar nenhum caso em que sejam diferentes, independentemente de shoptconfigurações e valores em $1ou pattern, nem $?posteriormente. A única diferença é que $1não é expandido na saída ao executar sob xtrace.
Kusalananda

Respostas:

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 cadeias, não a correspondência de padrões.

Dentro de uma [[ ]]construção (do man bash):

Quando os operadores == e! = São usados, a sequência à direita do operador é considerada um padrão e corresponde de acordo com as regras descritas abaixo em Correspondência de Padrão . Se a opção de concha nocasematch estiver ativada, a correspondência será realizada sem considerar o caso de caracteres alfabéticos. O valor de retorno é 0 se a sequência corresponder (==) ou não (! =) Ao padrão e 1 caso contrário. Qualquer parte do padrão pode ser citada para forçá-lo a corresponder como uma sequência.

Dentro de um caseconstruto (de man bash, editado e ênfase meu):

maiúsculas e minúsculas na lista [[(] padrão [| padrão] ...) ;; ] ... esac
... tenta combiná-lo com cada padrão, por sua vez, usando as mesmas regras de correspondência da expansão do nome do caminho (consulte Expansão do nome do caminho abaixo). … Cada padrão examinado é expandido usando expansão de til, expansão de parâmetro e variável, substituição aritmética, substituição de comando e substituição de processo. Se a opção de concha nocasematch estiver ativada, a correspondência será realizada sem considerar o caso de caracteres alfabéticos.

Ambos Pattern Matchinge Pathname Expansionsão usados ​​para significar o mesmo dentro do manual do 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

Isso quote removalnão está explicitamente listado para a construção de caso.
O que funciona para corresponder exatamente a este (para o [[ … ]]):

Qualquer parte do padrão pode ser citada para forçá-lo a corresponder como uma sequência.

Use isso 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. Implícito extglob:

    Desde a versão 4.3 do bash

    Quando os operadores '==' e '! =' São usados, a sequência à direita do operador é considerada um padrão e é correspondida de acordo com as regras descritas abaixo em Correspondência de Padrão, como se a opção shell extglob estivesse ativada .

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

  2. Implícito |:

    A sintaxe para o caso é:

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

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

    Como isso:

    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 abcde, como 1234ou aabee, mas não 12aou b23.

    A [[funcionará de forma equivalente se regex (veja var p3) for usado:

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

fonte