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.