Vorrei chiedere:
Perché è echo {1,2,3}
espanso a 1 2 3 che è un comportamento previsto, mentre echo [[:digit:]]
ritorna [[:digit:]]
mentre mi aspettavo che stampasse tutte le cifre da 0
a 9
?
Vorrei chiedere:
Perché è echo {1,2,3}
espanso a 1 2 3 che è un comportamento previsto, mentre echo [[:digit:]]
ritorna [[:digit:]]
mentre mi aspettavo che stampasse tutte le cifre da 0
a 9
?
Risposte:
Perché sono due cose diverse. Questo {1,2,3}
è un esempio di espansione di parentesi graffe . Il {1,2,3}
costrutto viene espanso dalla shell , prima echo
ancora di vederlo. Puoi vedere cosa succede se usi set -x
:
$ set -x
$ echo {1,2,3}
+ echo 1 2 3
1 2 3
Come puoi vedere, il comando echo {1,2,3}
è espanso in:
echo 1 2 3
Tuttavia, [[:digit:]]
è una classe di caratteri POSIX . Quando lo dai echo
, la shell lo elabora anche per primo, ma questa volta viene elaborato come un glob shell . funziona allo stesso modo come se si esegue, echo *
che stamperà tutti i file nella directory corrente. Ma [[:digit:]]
è un glob shell che corrisponderà a qualsiasi cifra. Ora, in bash, se un glob di shell non corrisponde a nulla, verrà espanso su se stesso:
$ echo /this*matches*no*files
+ echo '/this*matches*no*files'
/this*matches*no*files
Se il glob corrisponde a qualcosa, verrà stampato:
$ echo /e*c
+ echo /etc
/etc
In entrambi i casi, echo
stampa qualunque cosa la shell gli dica di stampare, ma nel secondo caso, poiché il glob corrisponde a qualcosa ( /etc
) viene detto di stampare quel qualcosa.
Quindi, dal momento che non hai file o directory il cui nome è costituito esattamente da una cifra (che è ciò [[:digit:]]
che corrisponderebbe), il glob viene espanso su se stesso e ottieni:
$ echo [[:digit:]]
[[:digit:]]
Ora prova a creare un file chiamato 5
ed eseguendo lo stesso comando:
$ echo [[:digit:]]
5
E se ci sono più file corrispondenti:
$ touch 1 5
$ echo [[:digit:]]
1 5
Questo è (in qualche modo) documentato nella man bash
spiegazione delle nullglob
opzioni che disattivano questo comportamento:
nullglob
If set, bash allows patterns which match no files (see
Pathname Expansion above) to expand to a null string,
rather than themselves.
Se imposti questa opzione:
$ rm 1 5
$ shopt -s nullglob
$ echo [[:digit:]] ## prints nothing
$
shopt -s failglob
per ottenere un comportamento più utile simile a quello delle conchiglie moderne come zsh
o fish
.
failglob
. nullglob
può causare problemi imprevisti, ad esempio quando si incolla un URL che sembra avere un ?
.
nullglob
per dimostrare che il modello viene interpretato come un glob dalla shell.
{1,2,3}
è espansione di parentesi graffe , si espande alle parole elencate senza riguardo al loro significato.
[...]
è un gruppo di caratteri, utilizzato nell'espansione del nome file (o jolly o glob) in modo simile all'asterisco *
e al punto interrogativo ?
. Corrisponde a qualsiasi singolo carattere elencato all'interno o ai caratteri membri dei gruppi denominati, ad esempio [:digit:]
se sono elencati. Il comportamento predefinito della maggior parte delle shell è di lasciare il carattere jolly così com'è se non ci sono file corrispondenti.
(Nota che non puoi davvero trasformare un carattere jolly / modello nell'insieme di stringhe che corrisponderebbe. L'asterisco può corrispondere a qualsiasi stringa di qualsiasi lunghezza, quindi espandere qualsiasi modello che lo contiene produrrebbe un elenco infinito di stringhe.)
Così:
$ bash -c 'echo [[:digit:]]' # bash leaves it as-is
[[:digit:]]
$ zsh -c 'echo [[:digit:]]' # zsh by default complains if no match
zsh:1: no matches found: [[:digit:]]
$ touch 1 3 d i g t
$ bash -c 'echo [[:digit:]]' # now there are two matches
1 3 # note that d, i, g and t do NOT match
Ma ancora:
$ bash -c 'echo {1,2,3}'
1 2 3
Entrambi sono espansi dalla shell , non importa se il comando che stai eseguendo è ls
, o echo
o rm
. Inoltre, se uno di questi è citato, non verranno espansi:
$ bash -c 'echo "[[:digit:]]"' # even though matching files still exist
[[:digit:]]
$ bash -c 'echo "{1,2,3}"'
{1,2,3}
[[:digit:]]
prima di passarlo echo
, quindi echo
non vede mai [[:digit:]]
, vede solo 1 3
. Puoi vederlo in azione eseguendo set -x
che stamperà i comandi effettivi in esecuzione (esegui set +x
per spegnerlo di nuovo).
echo
non cerca i file, lo fa la shell , prima di eseguire il file echo
.
{1,2,3}
(e ad esempio {1..3}
sono espansioni di parentesi graffe . Vengono interpretate dalla shell prima dell'esecuzione del comando.
[[:digit:]]
è un token corrispondente al modello , ma non lo si utilizza in una posizione con alcun file corrispondente a quel modello. Se si utilizza una corrispondenza del modello che non ha corrispondenze, si espande da sola:
$ echo [[:digit:]]; touch 3; echo [[:digit:]]
[[:digit:]]
3