Por que um espaço em branco é necessário entre "[[" e "-e xxx" em ksh?

9

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?

AcBap
fonte
2
[[é uma palavra-chave - presumivelmente árvore de análise do shell requer que palavras-chave devem ser delimitadas por espaços em branco
steeldriver
Outra maneira de analisar é provavelmente que você poderia escrever uma função [[-, como o shell saberia analisar "fazer a bandeira e em [[" em vez de "fazer a função [[- em e").
pbhj

Respostas:

15

A explicação mais simples seria, porque no manual parece que [[ expression ]]deve haver espaço entre [[e expressione 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. kshprocuraria por esse comando na lista de operadores internos e especiais (como forou 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 -eparte [[-eseria 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 kshdesenvolvimento for retomado ou pareça haver um fork ou clone (como mkshou pdksh), alguém implementará uma sintaxe sem espaço [[-e.

Veja também:

Sergiy Kolodyazhnyy
fonte
3
Acho que a citação manual do ksh está realmente no local. É semelhante a qualquer outra linguagem de programação: em C, charé uma palavra-chave, mas você ainda não pode escrever charsomeletter = 'A';e espera que o analisador pare depois de ver char.
Peter - Restabelece Monica
Eu acho que a principal diferença entre o seu exemplo de "char" e o símbolo "[[" na pergunta é que, um identificador alfanumérico, em geral, precisa de um delimitador de espaço para se separar dos outros; símbolos especiais como tokens, por outro lado, não precisam de delimitadores de espaço.
AcBap
Exatamente. Não sei se a comparação com C é útil. Em C, pode-se dizer i--<=++j, e o compilador não tem problema em analisar isso como i -- <= ++ j.
24519 Scott Scott
@ Scott Acho que a comparação C é útil (e é meio secundário que os identificadores C apenas permitam alfanuméricos e _). O Ksh tem (como operador e (echo hello)analisa como ( echo hello ). Tem if, {, [[, e outras palavras-chave , e ifx, {xe [[xsão cada um símbolo - como a forma como charsomeletteré um símbolo C. Por outro lado, um não citado (xem 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 .
Eliah Kagan
2

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 testdará 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 [nem [[é um comando, não. A palavra completa [[-eé.
  • O if [[-etorna um teste para verdadeiro / falso. Então , [[-eexiste 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. ;-)

Rinzwind
fonte
"% help [[" diz que "[[" é um comando condicional. Por que o analisador não pode parar assim que encontra o padrão "[[" e o trata como o início de um comando condicional?
AcBap
@Myloti [[-eé um nome de comando potencialmente válido (se de aparência estranha).
Gordon Davisson
2

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 .

WinEunuuchs2Unix
fonte
Observe que o OP perguntou sobre o kshshell na pergunta. Bash empresta [[operador kshe nesta questão o seu comportamento é idêntico, mas eu seria cauteloso de pressupostos como há algumas diferenças significativas entre kshe bashcomportamento 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
Sergiy Kolodyazhnyy
@SergiyKolodyazhnyy Eu estava sendo oportunista usando o shellcheck para destacar as [[regras internas. Eu não estava de maneira alguma inferindo que kshera um shell que shellcheckpoderia encontrar erros. Espero que outros apreciem kshe bashsejam dois intérpretes diferentes. Obrigado por mencionar isso. Também digno de nota [[é um bash embutido enquanto [é um comando externo.
WinEunuuchs2Unix
Na verdade, [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
Sergiy Kolodyazhnyy
2
Shellcheck sabe 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
Eliah Kagan
11
@EliahKagan Na verdade, eu postei uma pergunta sobre L & L para obter uma resposta precisa sobre o que POSIX pensa sobre esta situação unix.stackexchange.com/questions/526574/... linguagem Shell é suficiente complexo por isso espero que alguém pode explicar em termos mais formais
Sergiy Kolodyazhnyy
1

[[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, kshmas 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.

DarkDust
fonte
0

Uma vez que [[-epode 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 einclusive), 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.


fonte