Não há uma boa razão para
[[ $a = a|b ]]
Deve relatar um erro em vez de testar se $ a é a string a|b , enquanto [[ $a =~ a|b ]] não retorna um erro.
A única razão é que | é geralmente (fora e dentro de [[ ... ]] ) um caractere especial. Nesse [[ $a = position, bash espera um tipo de token que é normal WORD como os argumentos ou alvos de redirecionamentos em uma linha de comando normal do shell (mas como se a opção extglob tinha sido ativado desde bash 4.1).
(por WORD aqui, eu me refiro a uma palavra em uma gramática de shell hipotética como o descrito pela especificação POSIX , que é algo que o shell iria analisar como um token em uma linha de comando simples, não outra definição de palavras como a inglesa de uma sequência de letras ou uma sequência de caracteres não espaçadores. foo"bar baz" , $(echo x y) , são dois desses WORD s).
Em uma linha de comando normal do shell:
echo a|b
É echo a canalizado para b . a|b não é um WORD , são três tokens: um a WORD , um | token e um b WORD token.
Quando usado em [[ $a = a|b ]] , bash espera um WORD que obtém ( a ), mas depois encontra um token | inesperado que causa o erro.
Curiosamente, bash não se queixa em:
[[ $a = a||b ]]
Porque agora é um token a seguido por um token || seguido por b , por isso é analisado da mesma forma que:
[[ $a = a || b ]]
Que está testando que $a é a ou que a b está vazia.
Agora, em:
[[ $a =~ a|b ]]
bash não pode ter a mesma regra de análise. Ter a mesma regra de análise significaria que o acima daria um erro e que seria necessário citar o | para garantir que a|b seja uma única PALAVRA . Mas, desde o bash 3.2, se você fizer:
[[ $a =~ 'a|b' ]]
Isso não corresponde mais ao a|b regexp, mas ao a\|b regexp. Ou seja, a citação de shell tem o efeito colateral de remover o significado especial dos operadores de expressão regular. É um recurso, portanto, o comportamento é semelhante ao [[ $a = "?" ]] one, mas os padrões de curinga (usados em [[ $a = pattern ]] ) são shell WORDS (usados em globs, por exemplo), enquanto os regexps não são. / p>
Portanto, bash tem que tratar todos os operadores regexp estendidos que normalmente são caracteres de shell especiais como | , ( , ) diferentemente ao analisar um argumento do operador =~ .
Ainda assim, observe que enquanto
[[ $a =~ (ab)*c ]]
agora funciona,
[[ $a =~ [)}] ]]
não. Você precisa de:
[[ $a =~ [\)}] ]]
[[ $a =~ [')'}] ]]
Qual das versões anteriores de bash corresponderia incorretamente na barra invertida. Aquele foi corrigido, mas
[[ $a =~ [^]')'] ]]
O não corresponde à barra invertida como deveria, por exemplo. Porque bash não percebe que ) está entre colchetes, então escapa a ) para resultar em [^]\)] regexp que corresponde a qualquer caractere, mas ] , \ e ) .
ksh93 tem erros muito piores nessa frente.
Em zsh , é uma palavra de shell normal que é esperada e a citação de operadores de expressão regular não afeta o significado de operadores de expressão regular.
[[ $a =~ 'a|b' ]]
Está correspondendo à a|b regexp.
Isso significa que o =~ também pode ser adicionado ao comando [ / test :
[ "$a" '=~' 'a|b' ]
test "$a" '=~' 'a|b'
(também trabalhe em yash . O =~ precisa ser citado em zsh , já que =something é um operador de shell especial).
bash 3.1 costumava se comportar como zsh . Ele mudou em 3.2, provavelmente para alinhar com ksh93 (mesmo que bash tenha sido o shell que primeiro criou [[ =~ ]] ), mas você ainda pode fazer BASH_COMPAT=31 ou shopt -s compat31 para reverter para o comportamento anterior ( exceto que, enquanto [[ $a =~ a|b ]] retornaria um erro em bash 3.1, ele não existe mais em bash -O compat31 com versões mais recentes de bash ).
Espero que fique claro por que eu disse que as regras eram confusas e por que usar:
[[ $a =~ $var ]]
ajuda a incluir com portabilidade para outros shells.