Ad esempio, il seguente comando non funziona:
if [[-e xyz]]; then echo File exists;fi
ksh dà il seguente errore
[[-e: command not found
È perché "[[-" è ambiguo?
Ad esempio, il seguente comando non funziona:
if [[-e xyz]]; then echo File exists;fi
ksh dà il seguente errore
[[-e: command not found
È perché "[[-" è ambiguo?
Risposte:
La spiegazione più semplice sarebbe, perché nel manuale appare come [[ expression ]]
così ci deve essere spazio tra [[
e expression
e chiusura ]]
. Ma ovviamente possiamo tentare di esaminarlo più in profondità. Le conchiglie hanno una grammatica alquanto complessa e si basano fortemente sul concetto di "scissione delle parole". In particolare, il manuale di ksh (1) afferma:
La shell inizia ad analizzare il suo input suddividendolo in parole. Le parole, che sono sequenze di caratteri, sono delimitate da caratteri di spazi bianchi non quotati (spazio, tabulazione e nuova riga) o meta-caratteri (<,>, |,;, &, (e)). A parte la delimitazione delle parole, gli spazi e le schede vengono ignorati, mentre le nuove righe di solito delimitano i comandi.
Quindi, come indicato nel manuale, la sequenza di caratteri [[-e
è considerata una parola shell. ksh
cercherebbe tale comando nell'elenco di incorporati e operatori speciali (come for
o while
), quindi cercherebbe un comando esterno - e voilà - nessuno trovato, quindi c'è un messaggio di errore sul comando non trovato.
In questo caso [[
non sono nemmeno meta-caratteri speciali. Se lo fossero, la -e
parte in [[-e
sarebbe considerata una parola shell, non un argomento a [[
sé. Tuttavia, [[
viene descritto come comando composto e il manuale dice quanto segue:
I comandi composti vengono creati utilizzando le seguenti parole riservate: queste parole vengono riconosciute solo se non quotate e se vengono utilizzate come prima parola di un comando (ovvero, non possono essere precedute da assegnazioni di parametri o reindirizzamenti):
Quindi, per [[
essere riconosciuto come comando composto, deve essere una prima parola di un comando o di un elenco di comandi, che per definizione precedente di una "parola" implica che deve essere separato da spazi, tabulazioni o newline da altri parole / argomenti.
Nei commenti hai chiesto : "Perché il parser non può fermarsi non appena trova" [["pattern, e lo considera come l'inizio di un comando condizionale?" La risposta breve è probabilmente perché 1) influenza della [
sintassi poiché era originariamente un comando esterno, e per la conformità allo standard POSIX esiste ancora oggi come comando esterno, e 2) perché il parser della shell è costruito così. Shell parser è in grado di riconoscere altri caratteri speciali non delimitati da spazi: echo $((2+2))
e (echo foobar)
funziona perfettamente. Forse in futuro quando lo ksh
sviluppo riprende o sembra che ci sia un fork o un clone (come mksh
o pdksh
) qualcuno implementerà la sintassi senza spazio [[-e
.
[[
viene chiamata "parola riservata" (vedere la sezione 2.9 del linguaggio di comando Shell ). Per definizione POSIX, la parola riservata deve essere delimitata da spazi. Al contrario (
è un operatore, e gli stati standard nella sezione 2.9 "le rappresentazioni includono la spaziatura tra i token in alcuni punti in cui <blank>
s non sarebbe necessario (quando uno dei token è un operatore)". Vedi la discussione correlata: POSIX Shell Grammar: Perché Brace Group ha bisogno del primo spazio, ma il Subshell no?char
è una parola chiave ma non puoi ancora scrivere charsomeletter = 'A';
e aspettarti che il parser si fermi dopo aver visto char
.
i--<=++j
, e il compilatore non ha problemi a analizzarlo come i -- <= ++ j
.
_
). Ksh ha (
come operatore e (echo hello)
analizza come ( echo hello )
. E ' if
, {
, [[
, e altre parole chiave , e ifx
, {x
e [[x
sono ciascuna un gettone - come come charsomeletter
è un gettone C. Al contrario, un non quotato (x
in ksh è di due token - come i--
è due token in C. Ciò che concettualmente significa anche essere un operatore differisce tra shell in stile Bourne (come ksh) e C. Ma Peter A. Schneider ha sottolineato un vera somiglianza lessicale .
Ciò che è importante notare è che [
è un comando e [[
su cui si basa la parola chiave shell in bash e ksh [
.
[[
è usato in modo simile a [
. A volte si può anche sostituire [
]
con [[
]]
senza alcun cambiamento nel comportamento. Con [
, come qualsiasi comando, uno spazio è obbligatorio tra il comando e i suoi argomenti. Lo stesso vale per [[
.
Avevamo solo /usr/bin/[
. Ora la maggior parte delle shell sono [
integrate per l'efficienza, ma la sintassi è la stessa. Nelle shell che forniscono [[
, funziona come un'alternativa più versatile a [
.
Ecco la descrizione di [
in 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 `['.
Quindi [
equivale a test
(a parte aspettarsi un ]
argomento alla fine). help test
fornirà ulteriori dettagli al riguardo. Puoi confrontare questo con help [[
.
C'è anche una pagina man per il comando esterno [
( man \[
).
In caso di
if [[-e
[
, né [[
è un comando, lì. La parola completa [[-e
è.if [[-e
rende un test per vero / falso. Quindi esiste un comando [[-e
? È perché "[[-" è ambiguo?
Sì. O no. [[-e
è quello che è: niente che la shell capisca, quindi presume che sia un suo comando. ;-)
[[-e
è un nome comando potenzialmente valido (se strano).
Lo spazio è un deliminatore ed è necessario. Come puoi vedere da 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 ]].
(Sia ksh che bash supportano [[
e non funzionano senza lo spazio. Shellcheck fornisce esattamente quell'output con script ksh e bash simili contenenti quella riga errata.)
Perché i criminali sono necessari ha a che fare con token e lessici .
ksh
shell nella domanda. Bash prende in prestito l' [[
operatore da ksh
e in questa domanda il loro comportamento è identico, ma sarei prudente nei confronti delle ipotesi in quanto vi sono alcune differenze significative tra ksh
e bash
comportamento e interni. Questo è solo perché shellcheck funziona su bash script, potrebbe non essere necessariamente lo strumento appropriato per gli script ksh. Solo qualcosa da tenere a mente quando ci si avvicina a diverse conchiglie
[[
regole integrate. In nessun modo deducevo che ksh
fosse una shell in cui si shellcheck
potevano trovare errori. Spero che gli altri apprezzino ksh
e bash
siano due interpreti diversi. Grazie per averlo menzionato. Da notare anche [[
un bash incorporato mentre [
è un comando esterno.
[
è anche un built-in in bash :) manpages.ubuntu.com/manpages/bionic/man7/bash-builtins.7.html Ma non sei lontano - [
o test
deve essere un comando esterno. Ai tempi della shell Bourne originale [
era in realtà un comando esterno, ed esiste ancora oggi /usr/bin/[
perché POSIX richiede che sia un comando esterno pubs.opengroup.org/onlinepubs/009695399/utilities/test.html Pochi sono necessari a POSIX per essere incorporato pubs.opengroup.org/onlinepubs/009695399/idx/sbi.html Builtin [
è per l'efficienza
ksh
; usa un hashbang o -s ksh
. Btw, ksh e bash hanno [[
"built-in", ma è una parola chiave , a differenza di [
, che è una shell incorporata . (ksh :; whence -v [ [[
bash type [ [[
:) I builtin e i comandi esterni sono sintatticamente simili. Un modo [[
diverso da [
quello è quello di [[
sopprimere alcune espansioni nei suoi argomenti, che non potrebbe essere incorporato - tanto quanto {
non potrebbe fare il raggruppamento se fosse incorporato. Questo può essere il motivo per cui {x
(o [[x
) essere un token sembra strano. Penso che sia giusto dire che i builtin e le parole chiave sono lessicali ma non sintatticamente simili. @SergiyKolodyazhnyy
[[
può essere un comando esterno! Cioè, potrebbe essere un programma e non una sintassi supportata direttamente dalla shell. Supportare una sintassi senza spazio sarebbe possibile per ksh
ma fallirebbe su sistemi con esterno, [[
quindi per motivi di compatibilità è meglio mantenere lo spazio richiesto.
Poiché [[-e
può partecipare all'espansione della shell (può essere usato come
echo [[-e]*
al fine di elencare tutti i file che iniziano con una lettera tra [
e e
inclusivamente), sarebbe un disastro completo se [
e ]
fossero caratteri speciali che non partecipano alla normale suddivisione delle parole governata da spazi bianchi.
[[
è una parola chiave - presumibilmente l'albero di analisi della shell richiede che le parole chiave debbano essere delineate da spazi bianchi