Con bash, configura le impostazioni glob in modo che le corrispondenze mancanti non generino un errore:
shopt -u failglob # avoid failure report (and discarding the whole line).
shopt -s nullglob # remove (erase) non-matching globs.
ls ?c c?
Il punto interrogativo è un carattere globale che rappresenta un singolo carattere. Dal momento che vuoi nomi di file a due caratteri, uno di questi deve essere un c
, quindi è il primo carattere o l'ultimo carattere.
Con shopt -s dotglob
questo farebbe emergere anche un file chiamato .c
.
Se non ci sono file corrispondenti, l'impostazione di queste opzioni della shell provoca la rimozione di tutti gli argomenti, risultando in un semplice ls
-- elenca qualsiasi cosa per impostazione predefinita.
Usa invece questo:
shopt -s nullglob ## drop any missing globs
set -- ?c c? ## populate the [email protected] array with (any) matches
if [ $# -gt 0 ] ## if there are some, list them
ls -d "[email protected]"
fi
Penso che il modo migliore sia usare find
:
find . -type f -name '*c*' -name '??'
Questo cercherà in modo ricorsivo. Per elencare solo i file nella directory corrente:
find . ! -name . -prune -type f -name '*c*' -name '??'
Se i file esistono (e non hanno caratteri di escape come \c
), puoi utilizzare un glob:
echo ?c c?
che corrisponderà ai file che hanno un carattere (?
) seguito da un c
o che hanno un c
seguito da un carattere (?
).
Ma fallirà con i nomi di file che iniziano con un trattino come -n
o -e
o con caratteri backslash come \c
o \n
con echo
sia come -e
e -n
sono speciali per (alcune shell) echo
e \c
o \n
verrebbe interpretato come una sequenza di escape (\c
termina l'output e \n
stampa una nuova riga, non i caratteri letterali \n
in alcune implementazioni shell di echo e con bash quando l'opzione shopt -s xpg_echo
è impostato). Altre applicazioni o utilità (come ls
) avrà alcune altre opzioni e potrebbe non riuscire con molti altri caratteri avviati da trattino o interpretare altri caratteri di escape nei nomi di file.
Elencherà anche un file chiamato cc
due volte.
-
Se un file può iniziare con un trattino (come -n), usa:
$ ls -d -- ?n -n
O meglio:
$ ls -d ./?n ./-n
Avvertenze
-
Glob non corrisponde a nessun file.
-
Se i file non esistono, il glob non verrà espanso e verrà stampato nella sua forma originale.
$ echo ?m m? ?m m?
-
Tranne in zsh.
$ zsh -c 'echo ?m m?' zsh:1: no matches found: ./m?
La shell uscirà con un errore.
Questo comportamento è controllato danomatch
di Zsh opzione.$ zsh -c 'setopt +o nomatch; echo ?m m?' ?m m?
Oppure:
$ zsh -c 'echo ?m(N) m?(N)' ?m m?
Ma questo stamperà
mm
due volte. -
In bash potresti ottenere un risultato simile a zsh se l'opzione shell
failglob
è impostato:$ bash -c 'shopt -s failglob; echo ?m m?' bash: no match: ?m
Ma lo script non si fermerà, la shell non uscirà. Bene, tecnicamente, la riga in cui viene utilizzato il glob non viene eseguita ulteriormente, ma l'esecuzione riprende dalla riga di script successiva.
-
In bash puoi impostare l'opzione
nullglob
per rimuovere i glob che non corrispondono:$ bash -c 'shopt -s nullglob; echo ?m m? done' done
-
-
Usando ls (simile ad altri programmi)
-
Con i file corrispondenti non ci saranno problemi, ma corrisponderanno (ed elencheranno) anche le directory:
$ ls ?c c? bc cz sc ac: hjk
Meglio usare
-d
conls
(le directory saranno incluse ma non espanse).$ ls -d ?c c? ac bc cz sc
-
I glob non riescono a trovare una corrispondenza con un file (o una directory)
Poi
ls
riporterà un errore$ ls ?m m? ls: cannot access '?m': No such file or directory
Altri programmi potrebbero (probabilmente) non fare controlli simili.
-
Usando
nullglob
con bash darà un elenco vuoto a ls, quindi elencherà i contenuti o il pwd come se solols
è stato eseguito:$ ls ?m m? ac a.out cz 3 b sc ab bc
Questo potrebbe essere evitato usando
./
$ ls -d ./?m ./m? ls: cannot access './?m': No such file or directory ls: cannot access './m?': No such file or directory
-
Oppure puoi usare una regex find (ma attraverserà le sottodirectory con prune
missing) (Notare che questo non ripeterà un file chiamato cc):
$ find . ! -name . -prune -regex '.*/\(.c\|c.\)'
./ac
./cc
./sc
./bc
./cz