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 0a 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 0a 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 echoancora 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, echostampa 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 5ed 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 bashspiegazione delle nullglobopzioni 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 failglobper ottenere un comportamento più utile simile a quello delle conchiglie moderne come zsho fish.
failglob. nullglobpuò causare problemi imprevisti, ad esempio quando si incolla un URL che sembra avere un ?.
nullglobper 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 echoo 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 echonon vede mai [[:digit:]], vede solo 1 3. Puoi vederlo in azione eseguendo set -xche stamperà i comandi effettivi in esecuzione (esegui set +xper spegnerlo di nuovo).
echonon 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