16.2. find: cerca file in base a determinati criteri

find è un veterano tra i comandi Unix. Il suo scopo è analizzare ricorsivamente una o più directory e cercare all'interno di esse i file che corrispondono a determinati criteri. Sebbene sia molto utile, la sua sintassi è veramente oscura, ed è necessario un certo impegno per utilizzarlo. La sintassi generale è:

find [opzioni] [directory] [criterio] [azione]

Se non specificate alcuna directory, find cercherà nella directory attuale. Se non specificate il criterio di ricerca, questo sarà considerato equivalente a "true", e quindi saranno trovati come validi tutti i file. Le opzioni, i criteri e le azioni sono così tanti che qui ne citeremo solo alcuni. Iniziamo con le opzioni:

  1. -xdev: Esclude dalla ricerca le directory che risiedono su altri filesystem.

  2. -mindepth <n>: Scende almeno <n> livelli al di sotto della directory specificata prima di iniziare la ricerca.

  3. -maxdepth <n>: Cerca i file che si trovano al massimo n livelli al di sotto della directory specificata.

  4. -follow: Segue i link simbolici corrispondenti a directory. Come comportamento predefinito, find non li segue.

  5. -daystart: In caso di controlli relativi al tempo (vedi sotto), considera come orario l'inizio del giorno attuale invece del valore predefinito (24 ore prima dell'orario attuale).

Un criterio può essere costituito da uno o più controlli atomici fra i molti disponibili; alcuni controlli utili sono:

  1. -type <tipo>: Cerca un determinato tipo di file; <tipo> può essere uno dei seguenti: f (file normale), d (directory), l (collegamento simbolico), s (socket), b (file in modalità a blocchi), c (file in modalità a caratteri) o p (pipe con nome).

  2. -name <modello>: Cerca i file i cui nomi corrispondono al <modello> indicato. Con questa opzione, il <modello> è considerato come un modello di meta-espansione (si veda il capitolo I caratteri speciali (meta-caratteri) e le espressioni regolari nella shell).

  3. -iname <modello>: Come -name, ma non distingue tra maiuscole e minuscole.

  4. -atime <n>, -amin <n>: Cerca i file che sono stati letti <n> giorni fa (-atime) o <n> minuti fa (-amin). Potete anche usare +<n> o -<n>, e in questo caso saranno cercati i file letti rispettivamente al massimo o al minimo <n> giorni/minuti fa.

  5. -anewer <file>: Cerca i file che sono stati letti più recentemente del file <file>.

  6. -ctime <n>, -cmin <n>, -cnewer <file>: Sono equivalenti a -atime, -amin e -anewer, ma vengono applicati alla data dell'ultima modifica del file.

  7. -regex <modello>: Come -name, ma il modello viene considerato come una espressione regolare.

  8. -iregex <modello>: Come -regex, ma non distingue tra maiuscole e minuscole.

Esistono molti altri tipi di controlli, fate riferimento alla pagina di manuale per ulteriori dettagli. Per combinare i controlli potete scegliere fra:

  1. <c1> -a <c2>: È vero se sono veri sia <c1> che <c2>; -a è implicito, quindi potete scrivere <c1> <c2> <c3> ... se volete che siano verificati tutti i controlli <c1>, <c2>, ...

  2. <c1> -o <c2>: È vero se almeno uno tra <c1> e <c2> è vero. Ricordate che -o ha una precedenza inferiore a -a, e quindi se volete, ad esempio, cercare i file che corrispondono ad almeno uno dei criteri <c1> e <c2> oltre che al criterio <c3>, dovrete usare le parentesi e scrivere ( <c1> -o <c2> ) -a <c3>. Dovrete usare una sequenza escape per le parentesi (disattivandole), perché altrimenti saranno interpretate dalla shell!

  3. -not <c1>: Inverte il controllo <c1>, pertanto -not <c1> è vero se <c1> è falso.

Infine, potete specificare una azione per ciascun file trovato. Le più frequentemente usate sono le seguenti:

  1. -print: Stampa il nome di ciascun file sullo standard output. Se non specificate alcuna azione, questo è il comportamento predefinito.

  2. -ls: Stampa sullo standard output l'equivalente del comando ls -ilds per ogni file trovato.

  3. -exec <comando>: Esegue il comando <comando> per ogni file trovato. La linea di comando <comando> deve terminare con un ;, per il quale deve essere utilizzata una sequenza escape in modo che la shell non lo interpreti; la posizione del file è rappresentata da {}. Per maggiori chiarimenti si vedano gli esempi di utilizzo.

  4. -ok <comando>: Come -exec, ma chiede conferma per ogni comando.

Ci siete ancora? Bene, ora facciamo un po' di pratica, è sempre il modo migliore per capire questo mostro. Supponiamo che vogliate trovare tutte le directory contenute in /usr/share. Dovrete scrivere:

find /usr/share -type d

Supponiamo che abbiate un server HTTP, e che tutti i vostri file HTML siano in /home/httpd/html, che è anche la vostra directory attuale. Volete cercare tutti i file il cui contenuto non è stato modificato da un mese a questa parte. Poiché possedete pagine create da diversi autori, alcuni file hanno l'estensione html mentre altri hanno l'estensione htm. Volete creare dei collegamenti a questi file nella directory /home/httpd/obsolete. Dovrete quindi scrivere:
find \( -name "*.htm" -o -name "*.html" \) -a -ctime -30 -exec ln {} /home/httpd/obsolete \;
[1]

Va bene, questo esempio è un po' complicato ed è necessaria una breve spiegazione. Il criterio è questo:
\( -name "*.htm" -o -name "*.html" \) -a -ctime -30
e fa quello che noi vogliamo: cerca tutti i file i cui nomi finiscono in .htm o .html "\( -name "*.htm" -o -name "*.html" \)", e (-a) che non sono stati modificati negli ultimi 30 giorni, che equivalgono più o meno a un mese (-ctime -30). Fate caso alle parentesi: in questo caso sono necessarie, perché -a ha una precedenza maggiore; se non le avessimo usate, sarebbero stati trovati tutti i file il cui nome finisce per .htm, più tutti i file il cui nome finisce per .html e che non sono stati modificati nell'ultimo mese, e questo non è quello che avevamo in mente. Notate anche che le parentesi sono disattivate dalla shell: se noi avessimo scritto ( .. ) invece di \( .. \), la shell le avrebbe interpretate e avrebbe cercato di eseguire -name "*.htm" -o -name "*.html" in una sotto-shell... Un'altra soluzione poteva essere quella di racchiudere le parentesi fra virgolette doppie o singole, ma una sbarretta retroversa (\) qui è preferibile perché dobbiamo isolare un solo carattere.

E, per finire, abbiamo il comando che deve essere eseguito su ogni file:

-exec ln {} /home/httpd/obsolete \;

Anche qui è necessario disattivare il ; dalla shell, altrimenti questa lo interpreterebbe come un separatore di comandi. Se non lo fate, find lamenterà la mancanza di un argomento -exec.

Un ultimo esempio: supponiamo di avere una directory /shared/images enorme, contenente tutti i tipi di immagini possibili. Normalmente si usa il comando touch per aggiornare l'orario di un file di nome stamp contenuto nella stessa directory, in modo da avere un riferimento temporale. Vogliamo cercare in questa directory tutte le immagini JPEG più recenti del file stamp, e poiché possedete immagini provenienti da diverse fonti, questi file potranno avere estensione jpg, jpeg, JPG o JPEG. Vogliamo anche evitare di cercare nella directory old. Vogliamo infine che questa lista di file ci venga spedita via email, e il nostro nome utente è mario:

find /shared/images -cnewer     \
     /shared/images/stamp       \
     -a -iregex ".*\.jpe?g"     \
     -a -not -regex ".*/old/.*" \
       | mail mario -s "Nuove immagini"

Ed ecco fatto! Naturalmente questo comando non è molto utile se dovete riscriverlo tutto ogni volta, e inoltre potreste volere che sia eseguito periodicamente... Potete fare così:

Note

[1]

Notate che per questo esempio è necessario che /home/httpd e /home/httpd/obsolete siano sullo stesso filesystem!


Tux on Star from MandrakeSoft Linux è un trademark registrato di Linus Torvalds. Tutti gli altri copyright e trademark appartengono ai rispettivi proprietari.
A meno che non sia specificato diversamente, tutto il contenuto di queste pagine e tutte le immagini sono Copyright MandrakeSoft S.A. e MandrakeSoft Inc. 2000.
http://www.linux-mandrake.com/