Achei algo surpreendente quando estou escrevendo para algum script bash hoje. Eu o fixei neste exemplo mínimo.
[[ a>b ]]; echo $?
Com base no meu entendimento, como não há espaços ao redor do >
, isso deve testar se a string a>b
não está vazia e deve retornar um código de erro de 0
. No entanto, o comando acima ecoa 1
em ambas as versões do bash que testei (detalhes abaixo).
Eu também testei usando o antigo test
comando "good",
[ a>b ]; echo $?
echos 0
(testa se a string a
não está vazia e cria um arquivo vazio b
no meu diretório de trabalho atual, aparentemente >b
é tratado como um redirecionamento, o que é compreensível).
Então eu tentei mais algumas outras coisas
[[ b>a ]]; echo $?
ecos0
e nenhum arquivo é criado.[[ b<a ]]; echo $?
ecos1
e nenhum arquivo é criado.[[ a<b ]]; echo $?
ecos0
e nenhum arquivo é criado.[ b>a ]; echo $?
echos0
e cria um arquivo vazioa
.[ b<a ]; echo $?
relata um erro de arquivo ausentea
e ecos1
devido a esse erro.[ a<b ]; echo $?
relata um erro de arquivo ausenteb
e ecos1
devido a esse erro.[[ a=b ]]; echo $?
echos0
porque está testando se a stringa=b
não está vazia como eu esperava.[ a=b ]; echo $?
ecos0
pelo mesmo motivo.[[ a==b ]]; echo $?
ecos0
pelo mesmo motivo.[ a==b ]; echo $?
ecos0
pelo mesmo motivo.[[ a!=a ]]; echo $?
e[ a!=a ]; echo $?
ambos ecoam0
(esperado)
Portanto, parece que apenas os espaços ao redor de <
e >
podem ser omitidos em uma expressão condicional, mas não =
ou ==
ou !=
se a intenção for fazer comparação de strings. Mas por que ele é projetado dessa maneira? Também pode ser minha omissão, mas isso não parece estar documentado em nenhum lugar do manual do bash.
Meu problema original é tentar usar >
sem escape como parte de um padrão em uma expressão condicional ( [[ ... ]]
). Originalmente, pensei que, desde que não envolvesse o >
com espaços, poderia usá-lo sem escape sem problemas, porque não faz sentido (e acaba sendo impossível após alguns testes) fazer redirecionamentos dentro de um condicional expressão também.
No entanto, verifica-se que não é o caso. É claro que a solução simples é apenas escapar disso >
e escrever \>
, mas não entendo por que isso é necessário.
Aqui estão as versões do bash que usei para testar,
GNU bash, version 5.2.2(1)-release (aarch64-unknown-linux-android)
e
GNU bash, version 3.2.25(1)-release (x86_64-redhat-linux-gnu)
Meus experimentos são realizados sob env -i bash --norc --noprofile
.
Isso se resume ao fato de que caracteres como
&
,;
,(
,|
,<
,>
, tab são metacaracteres na sintaxe do shell. Eles formam seus próprios tokens (ou podem se combinar em mais tokens como;;
,|&
,||
...). Isso é diferente de caracteres como[
,{
,!
¹,-
e=
que são semelhantes a letras² e números neste.Dentro da
[[...]]
construção do shell Korn, o shell entende uma microlinguagem separada, mas segue aproximadamente as mesmas regras de tokenização do lado de fora.Pela mesma razão, fora de
[[...]]
, você pode fazer:e não precisa escrever:
Ou mesmo coisas como
if((1))then<file&&(uname)fi
.Aqui você pode fazer:
como
(
, e são metacaracteres de shell>
.|
)
Os metacaracteres devem ser citados (seja com
'...'
,"..."
,\
,$'...'
...) para serem interpretados literalmente.[[x]]
não funcionaria porque o shell vê apenas um[[x]]
token. E a[[
palavra-chave para iniciar essa[[...]]
construção nem seria reconhecida.[[ a==b ]]
não é o mesmo que[[ a == b ]]
, como na[[...]]
microlinguagem,a==b
é um único token, então isso é interpretado da mesma forma que[[ -n 'a==b' ]]
.[
em si é apenas um comando normal, por isso é analisado da mesma forma que outro comando.é executado
[
coma
e]
como argumento e sua saída é redirecionada parab
, mesmo que[ a ] > b
, assim comoecho a>b ]
é igual aecho a ] > b
ou>b echo a ]
.Observe que é diferente por dentro
((...))
(também do ksh), que também vem com sua própria microlinguagem semelhante a C, mas desta vez as regras de tokenização são diferentes. Por exemplo, você pode escrever((var=123+1))
ou((a==b))
e não precisa((var = 123 + 1))
de ou((a == b))
.¹
{
está envolvido na expansão de colchetes e também é uma palavra reservada do shell, mas processada após a tokenização, embora[
esteja envolvida na tokenização ao analisar atribuições.a[1 + 1]=foo
em bash ou ksh (não zsh) é analisado como uma palavra de atribuição, não como execuçãoa[1
com+
e1]=foo
como argumentos.!
é uma palavra reservada como{
, mas também está envolvida na expansão do histórico, embora seja apenas para invocações interativas do shell.² observe que as letras e
-
estão envolvidas em alguns dos[[...]]
operadores, como-nt
,-eq
,-lt
...