Não há uma boa razão para
[[ $a = a|b ]]
Deve relatar um erro em vez de testar se $ a é a a|bsequência, enquanto [[ $a =~ a|b ]]não retorna um erro.
A única razão é que |geralmente é (fora e dentro [[ ... ]]) um caractere especial. Nessa [[ $a =posição, bashespera-se um tipo de token que seja uma PALAVRA normal, como os argumentos ou os destinos de redirecionamentos em uma linha de comando normal do shell (mas como se oextglob opção tivesse sido ativada desde o bash 4.1).
(por WORD aqui, refiro-me a uma palavra em uma gramática hipotética de shell como a descrita pela especificação POSIX , isso é algo que o shell analisaria como um token em uma simples linha de comando do shell, não outra definição de palavras como o inglês um de uma sequência de letras ou de uma sequência de caracteres sem espaçamento. foo"bar baz", $(echo x y), são dois tais PALAVRA s).
Em uma linha de comando normal do shell:
echo a|b
É echo acanalizado para b. a|bnão é uma PALAVRA , são três fichas: uma a PALAVRA , um |token e um token da b PALAVRA .
Quando usado [[ $a = a|b ]], bashespera uma PALAVRA que ele recebe ( a), mas encontra um |token inesperado que causa o erro.
Curiosamente, bashnão se queixa em:
[[ $a = a||b ]]
Como agora é um atoken seguido de um ||token seguido de b, ele é analisado da mesma maneira que:
[[ $a = a || b ]]
Que está testando que $aé aou que a bstring é não vazio.
Agora em:
[[ $a =~ a|b ]]
bashnão pode ter a mesma regra de análise. Ter a mesma regra de análise significaria que o anterior causaria um erro e seria necessário citar que, |para garantir, a|bé uma única PALAVRA . Mas, desde o bash 3.2, se você fizer:
[[ $a =~ 'a|b' ]]
Isso não corresponde mais ao a|bregexp, mas ao a\|bregexp. Ou seja, a citação de shell tem o efeito colateral de remover o significado especial dos operadores regexp. É um recurso, portanto, o comportamento é semelhante ao [[ $a = "?" ]]padrão, mas os padrões curinga (usados em [[ $a = pattern ]]) são PALAVRAS shell (usados em globs, por exemplo), enquanto os regexps não.
Então, bashtem que tratar todos os operadores de expressões regulares estendidas que são de outra maneira normalmente especial caracteres shell como |, (, )diferente ao analisar um argumento da=~ operador.
Ainda assim, observe que enquanto
[[ $a =~ (ab)*c ]]
agora funciona
[[ $a =~ [)}] ]]
não. Você precisa:
[[ $a =~ [\)}] ]]
[[ $a =~ [')'}] ]]
O qual nas versões anteriores de bashcorresponderia incorretamente na barra invertida. Aquele foi consertado, mas
[[ $a =~ [^]')'] ]]
Será que não corresponder em barra invertida como deveria, por exemplo. Como bashfalha ao perceber que )está entre parênteses, escapa-o )para resultar em uma [^]\)]regexp que corresponde a qualquer caractere ], exceto , \e) .
ksh93 tem bugs muito piores nessa frente.
Em zsh, é uma palavra shell normal que é esperada e citar operadores regexp não afeta o significado de operadores regexp.
[[ $a =~ 'a|b' ]]
É compatível com a a|bregexp.
Isso significa =~que também pode ser adicionado ao comando [/ test:
[ "$a" '=~' 'a|b' ]
test "$a" '=~' 'a|b'
(também funciona yash. As =~necessidades devem ser citadas zshcomo =somethingum operador de shell especial).
bash 3.1 costumava se comportar como zsh. Ele mudou na versão 3.2, presumivelmente para se alinhar ksh93(embora tenha bashsido o shell que surgiu pela primeira vez [[ =~ ]]), mas você ainda pode fazer BASH_COMPAT=31ou shopt -s compat31reverter para o comportamento anterior (exceto que embora [[ $a =~ a|b ]]retornasse um erro na bashversão 3.1, isso não acontece mais) nas bash -O compat31versões mais recentes do bash).
Espero que esclareça por que eu disse que as regras eram confusas e por que usar:
[[ $a =~ $var ]]
ajuda inclusive com portabilidade para outras conchas.
|é especial) está ativada por padrão no lado direito de[[ $var = $pattern ]]. Seria interessante isolar as versões eshoptconfigurações de opções em que esse comportamento é visto - se são apenas aquelas em queextglobestá, por padrão ou configuração explícita, bem, aqui estamos.