Por exemplo, o seguinte comando não funciona:
if [[-e xyz]]; then echo File exists;fi
ksh fornece o seguinte erro
[[-e: command not found
Isso é porque "[[-" é ambíguo?
Por exemplo, o seguinte comando não funciona:
if [[-e xyz]]; then echo File exists;fi
ksh fornece o seguinte erro
[[-e: command not found
Isso é porque "[[-" é ambíguo?
Respostas:
A explicação mais simples seria, porque no manual parece que [[ expression ]]
deve haver espaço entre [[
e expression
e fechando ]]
. Mas é claro que podemos tentar analisá-lo mais profundamente. Os reservatórios têm uma gramática um tanto complexa e dependem muito do conceito de "divisão de palavras". Em particular, o manual do ksh (1) declara:
O shell começa a analisar sua entrada dividindo-a em palavras. As palavras, que são sequências de caracteres, são delimitadas por caracteres de espaço em branco sem aspas (espaço, tabulação e nova linha) ou metacaracteres (<,>, |,;, &, (e)). Além de delimitar palavras, espaços e tabulações são ignorados, enquanto novas linhas geralmente delimitam comandos.
Portanto, como declarado no manual, a sequência de caracteres [[-e
é considerada uma palavra concha. ksh
procuraria por esse comando na lista de operadores internos e especiais (como for
ou while
), depois procuraria por um comando externo - e pronto - nenhum encontrado, portanto, há uma mensagem de erro sobre o comando não encontrado.
Nesse caso, [[
também não são meta-caracteres especiais. Se fossem, a -e
parte [[-e
seria considerada uma palavra concha, não um argumento para [[
si mesma. No entanto, [[
é descrito como comando composto e o manual diz o seguinte:
Os comandos compostos são criados usando as seguintes palavras reservadas - essas palavras são reconhecidas apenas se não estiverem entre aspas e se forem usadas como a primeira palavra de um comando (ou seja, não poderão ser precedidas por atribuições ou redirecionamentos de parâmetros):
Portanto, para [[
ser reconhecido como um comando composto, ele deve ser a primeira palavra de um comando ou lista de comandos, que por definição anterior de uma "palavra" implica que ela deve ser separada por espaços, tabulações ou novas linhas de outras palavras / argumentos.
Você perguntou nos comentários : "Por que o analisador não pode parar assim que encontra o padrão" [["e trata isso como o início de um comando condicional?" A resposta curta é provavelmente porque 1) influência da [
sintaxe, uma vez que era originalmente um comando externo, e para a conformidade com o POSIX existe como comando externo ainda hoje, e 2) porque o analisador de shell é construído assim. O analisador de shell pode reconhecer outros caracteres especiais não delimitados por espaço: echo $((2+2))
e (echo foobar)
funciona perfeitamente bem. Talvez no futuro, quando o ksh
desenvolvimento for retomado ou pareça haver um fork ou clone (como mksh
ou pdksh
), alguém implementará uma sintaxe sem espaço [[-e
.
[[
é chamada de "palavra reservada" (consulte a seção 2.9 da Shell Command Language ). Pela definição do POSIX, a palavra reservada deve ser delimitada por espaços. Por outro lado, (
é um operador, e os estados padrão na seção 2.9 "as representações incluem espaçamento entre tokens em alguns lugares onde <blank>
s não seria necessário (quando um dos tokens é um operador)". Veja a discussão relacionada: POSIX Shell Grammar: Por que o Brace Group precisa do primeiro espaço, mas o subshell não?char
é uma palavra-chave, mas você ainda não pode escrever charsomeletter = 'A';
e espera que o analisador pare depois de ver char
.
i--<=++j
, e o compilador não tem problema em analisar isso como i -- <= ++ j
.
_
). O Ksh tem (
como operador e (echo hello)
analisa como ( echo hello )
. Tem if
, {
, [[
, e outras palavras-chave , e ifx
, {x
e [[x
são cada um símbolo - como a forma como charsomeletter
é um símbolo C. Por outro lado, um não citado (x
em ksh é dois tokens - como i--
é o caso de dois tokens em C. O que significa conceitualmente ser um operador difere entre as conchas no estilo Bourne (como ksh) e C. Mas Peter A. Schneider apontou uma semelhança lexical real .
O que é importante notar é que [
é um comando, e a palavra-chave shell [[
no bash e ksh é baseada [
.
[[
é usado de maneira semelhante a [
. Às vezes você pode até mesmo substituir [
]
com [[
]]
com nenhuma mudança no comportamento. Com [
, como qualquer comando, um espaço é obrigatório entre o comando e seus argumentos. O mesmo se aplica a [[
.
Nós costumávamos ter apenas /usr/bin/[
. Agora a maioria dos shells foi [
incorporada para obter eficiência - mas a sintaxe é a mesma. Nas conchas que fornecem [[
, ele funciona como uma alternativa mais versátil[
.
Aqui está a descrição para [
no bash:
$ help [
[: [ arg... ]
Evaluate conditional expression.
This is a synonym for the "test" builtin, but the last argument must
be a literal `]', to match the opening `['.
Então [
é equivalente a test
(além de esperar um ]
argumento no final). help test
dará ainda mais detalhes sobre isso. Você pode comparar isso com help [[
.
Há também uma página de manual para o comando externo [
( man \[
).
No caso de
if [[-e
[
nem [[
é um comando, não. A palavra completa [[-e
é.if [[-e
torna um teste para verdadeiro / falso. Então , [[-e
existe um comando ? Isso é porque "[[-" é ambíguo?
Sim. Ou não. [[-e
é o que é: nada que o shell entenda para assumir que é seu próprio comando. ;-)
[[-e
é um nome de comando potencialmente válido (se de aparência estranha).
O espaço é um deliminador e é necessário. Como você pode ver em shellcheck
:
$ shellcheck gmail-browse-msgs-algorithm.sh
In gmail-browse-msgs-algorithm.sh line 847:
[[$Today != "${DaysArr[ i + DAY_DELETED_ON_NDX ]}" ]] && continue
^-- SC1035: You need a space after the [[ and before the ]].
(O ksh e o bash suportam [[
e não funcionam sem o espaço. O Shellcheck fornece exatamente essa saída com scripts semelhantes do ksh e do bash que contêm essa linha de buggy.)
Por que os deliminadores são necessários tem a ver com tokens e léxicos .
ksh
shell na pergunta. Bash empresta [[
operador ksh
e nesta questão o seu comportamento é idêntico, mas eu seria cauteloso de pressupostos como há algumas diferenças significativas entre ksh
e bash
comportamento e internos. Isso ocorre porque o shellcheck funciona no script bash, pode não ser necessariamente a ferramenta apropriada para os scripts ksh. Só uma coisa a ter em mente quando se aproxima de escudos diferentes
[[
regras internas. Eu não estava de maneira alguma inferindo que ksh
era um shell que shellcheck
poderia encontrar erros. Espero que outros apreciem ksh
e bash
sejam dois intérpretes diferentes. Obrigado por mencionar isso. Também digno de nota [[
é um bash embutido enquanto [
é um comando externo.
[
também é um bash embutido :) manpages.ubuntu.com/manpages/bionic/man7/bash-builtins.7.html Mas você não está muito longe - [
ou test
é necessário que seja um comando externo. Na época original, o shell Bourne [
era de fato um comando externo, e ainda existe hoje em dia, /usr/bin/[
porque o POSIX exige que seja um comando externo pubs.opengroup.org/onlinepubs/009695399/utilities/test.html Poucos são exigidos pelo POSIX para ser embutido pubs.opengroup.org/onlinepubs/009695399/idx/sbi.html Builtin [
é para eficiência
ksh
; use um hashbang ou -s ksh
. Btw, ksh e bash foram [[
"incorporados", mas é uma palavra - chave , ao contrário [
, que é um shell embutido . (ksh :; whence -v [ [[
bash type [ [[
:) Builtins e comandos externos são sintaticamente iguais. Uma maneira de [[
diferir [
disso é que [[
suprime algumas expansões em seus argumentos, o que não poderia ser incorporado - tanto quanto {
não poderia fazer agrupamentos se fosse interno. Pode ser por isso que {x
(ou [[x
) ser um token parece estranho. Eu acho que é certo dizer que builtins e palavras-chave são lexicamente, mas não sintaticamente semelhantes. @SergiyKolodyazhnyy
[[
pode ser um comando externo! Ou seja, pode ser um programa e não uma sintaxe suportada diretamente pelo seu shell. Seria possível oferecer suporte a uma sintaxe sem espaço, ksh
mas ela falharia em sistemas externos, [[
portanto, por motivos de compatibilidade, é melhor manter o espaço necessário.
O BusyBox está fornecendo [[
como um comando externo , por exemplo.
Uma vez que [[-e
pode participar da expansão do shell (pode ser usado como
echo [[-e]*
para listar todos os arquivos que começam com uma letra entre [
e e
inclusive), seria uma bagunça completa se [
e ]
se caracteres especiais não participassem da divisão normal de palavras governada por espaço em branco.
[[
é uma palavra-chave - presumivelmente árvore de análise do shell requer que palavras-chave devem ser delimitadas por espaços em branco