Minha pergunta vem de Como o armazenamento da expressão regular em uma variável do shell evita problemas com a citação de caracteres especiais para o shell? .
Por que há um erro:
$ [[ $a = a|b ]] bash: syntax error in conditional expression: unexpected token `|' bash: syntax error near `|b'
Espera-se que dentro
[[ ... ]]
do segundo operando de=
um padrão globbing.Não
a|b
é um padrão de globbing válido? Você pode apontar qual regra de sintaxe viola?Alguns comentários abaixo apontam que
|
é interpretado como pipe.Mudando
=
para o padrão glob para=~
para o padrão regex faz o|
trabalho$ [[ $a =~ a|b ]]
Aprendi com o Learning Bash p180 em minha postagem anterior, que
|
é reconhecida como pipe no início da interpretação, mesmo antes de qualquer outra etapa de interpretação (incluindo a análise das expressões condicionais nos exemplos). Então, como pode|
ser reconhecido como operador regex ao usar=~
, sem ser reconhecido como pipe em uso inválido, assim como ao usar=
? Isso me faz pensar que o erro de sintaxe na parte 1 não significa que|
seja interpretado como um pipe.Cada linha que o shell lê da entrada padrão ou de um script é chamada de pipeline; ele contém um ou mais comandos separados por zero ou mais caracteres de barra vertical (|). Para cada pipeline que lê, o shell divide-o em comandos, configura a E / S para o pipeline e, em seguida, faz o seguinte para cada comando (Figura 7-1):
Obrigado.
|
é especial) está ativada por padrão no lado direito de[[ $var = $pattern ]]
. Seria interessante isolar as versões eshopt
configurações de opções em que esse comportamento é visto - se são apenas aquelas em queextglob
está, por padrão ou configuração explícita, bem, aqui estamos.pattern='a|b'
e expanda sem$pattern
aspas no RHS.Respostas:
Não há uma boa razão para
Deve relatar um erro em vez de testar se $ a é a
a|b
sequê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,bash
espera-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
canalizado parab
.a|b
não é uma PALAVRA , são três fichas: umaa
PALAVRA , um|
token e um token dab
PALAVRA .Quando usado
[[ $a = a|b ]]
,bash
espera uma PALAVRA que ele recebe (a
), mas encontra um|
token inesperado que causa o erro.Curiosamente,
bash
não se queixa em:Como agora é um
a
token seguido de um||
token seguido deb
, ele é analisado da mesma maneira que:Que está testando que
$a
éa
ou que ab
string é não vazio.Agora em:
bash
nã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:Isso não corresponde mais ao
a|b
regexp, mas aoa\|b
regexp. 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,
bash
tem 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
agora funciona
não. Você precisa:
O qual nas versões anteriores de
bash
corresponderia incorretamente na barra invertida. Aquele foi consertado, masSerá que não corresponder em barra invertida como deveria, por exemplo. Como
bash
falha 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.É compatível com a
a|b
regexp.Isso significa
=~
que também pode ser adicionado ao comando[
/test
:(também funciona
yash
. As=~
necessidades devem ser citadaszsh
como=something
um operador de shell especial).bash 3.1 costumava se comportar como
zsh
. Ele mudou na versão 3.2, presumivelmente para se alinharksh93
(embora tenhabash
sido o shell que surgiu pela primeira vez[[ =~ ]]
), mas você ainda pode fazerBASH_COMPAT=31
oushopt -s compat31
reverter para o comportamento anterior (exceto que embora[[ $a =~ a|b ]]
retornasse um erro nabash
versão 3.1, isso não acontece mais) nasbash -O compat31
versões mais recentes dobash
).Espero que esclareça por que eu disse que as regras eram confusas e por que usar:
ajuda inclusive com portabilidade para outras conchas.
fonte
[[ $a = a|b ]]
.a|b
não é um shell PALAVRA aqui, é aa
,|
eb
token. Comoecho a|b
não geraa|b
ou não expande uma|b
globo, é necessário citar isso|
, pois é um caractere de shell especial inválido nesse contexto.[[ $a = (a|b) ]]
funcionaria comoecho (a|b)
funcionaria como(a|b)
é um operador curinga zsh.Globs padrão ( "expansão filename") são:
*
,?
, e[ ... ]
.|
não é um operador glob válido nas configurações padrão (não extglob).Experimentar:
fonte
|
interpretado literalmente? Por que há um erro de sintaxe?|
não é um operador glob, portanto não é|
interpretado literalmente sem ser citado? Então, por que há um erro de sintaxe?|
é um caractere de controle; nunca é tratado como um caractere literal da mesma maneira que uma letra ou número.[[ $a = a
não é um comando válido cuja saída possa ser canalizada para outro processo (pelo menos é o que o shell pensou que você estava tentando fazer).Se você deseja que uma regex corresponda, o teste seria:
fonte