next up previous contents index
Next: tarl'archiviatore a nastro Up: Comandi buffi Previous: Comandi buffi

find, per cercare i file

Generalità

Tra i vari comandi visti finora, ce ne sono alcuni che permettono all'utente di scendere ricorsivamente nell'albero delle directory per fare qualche azione: gli esempi canonici sono ls -R e rm -R. Bene. find è il comando ricorsivo. Ogni volta che pensate ``Beh, devo fare questo e quest'altro su tutti questi tipi di file nella mia partizione'', dovreste pensare di usare find. In un certo senso il fatto che find trovi file è un effetto secondario: il suo vero compito è valutare.

La struttura di base del comando è la seguente:

find  percorso [...] espressione [...]

Questo almeno vale per la versione GNU; altre versioni non permettono di specificare più di un percorso, ed inoltre è molto raro che serva una cosa del genere. La spiegazione della sintassi del comando è piuttosto semplice: dite da dove volete che cominci la ricerca (la parte percorso; con GNU si può omettere e per default verrà presa in considerazione la directory corrente .), e che tipo di ricerca volete fare (la parte espressione).

Il comportamento standard del comando è piuttosto complicato, quindi vale la pena notarlo. Supponiamo che nella vostra home directory ci sia una directory butta, che contiene il file pippo. Digitate find . -name pippo (che come potete indovinare ricerca un file di nome pippo), e ottenete ...niente altro che il prompt. Il problema sta nel fatto che find è per default silenzioso: rende 0 se la ricerca è stata completata (trovando qualcosa o meno) o un valore non-zero se ci sono stati dei problemi. Questo non accade con la versione che trovate su Linux, ma è comunque utile da ricordare.

Espressioni

La parte delle espressioni può essere divisa in quattro gruppi di parole chiave diversi: opzioni, test, azioni ed operatori; ognuno di questi può restituire un valore vero o falso, insieme ad un effetto secondario. Le differenze tra i gruppi vengono mostrate più avanti.

opzioni
influiscono sulle operazioni generali di find, piuttosto che sul processo di un singolo file. Un esempio è -follow, che dice a find di seguire i link simbolici invece di dire semplicemente l'inode. Restituisce sempre il valore vero.

test
sono veri e propri controlli (ad esempio, -empty controlla se il file è vuoto), e possono restituire i valori vero o falso.

azioni
hanno anche un effetto secondario, il nome del file considerato. Possono restituire vero o falso anch'esse.

operatori
non restituiscono un valore (possono essere considerati veri per convenzione), e vengono usati per costruire espressioni compresse. Un esempio è -or, che prende l'O logico delle due sottoespressioni ai suoi lati. Notate che quando si mettono due espressioni una dopo l'altra, è sottinteso -and.

Notate che find si basa sulla shell per leggere la linea di comando, ciò significa che le parole chiave devono essere messe tra spazi, e soprattutto che molti caratteri devono essere trattati in modo da non essere corrotti dalla shell. Si può fare sia con la barra rovesciata che con le virgolette, singole e doppie; negli esempi le parole chiave a carattere singolo verranno quotate con la barra rovesciata, perché (almeno secondo me, ma sono io che scrivo queste note!) è il modo più semplice.

Opzioni

Ecco la lista di tutte le opzioni note alla versione GNU di find. Ricordate che restituiscono sempre il valore vero.

Test

I primi due test sono molto semplici da capire: -false restituisce solo il valore falso, mentre -true restituisce solo il valore vero. Altri test che non hanno bisogno della specifica di un valore sono -empty, che restituisce il valore vero se il file è vuoto, e la coppia -nouser / -nogroup, che restituiscono il valore vero se nessuna voce di /etc/passwd o /etc/group corrisponde all'id di utente/gruppo del proprietario del file. Questa è una cosa comune che accade nei sistemi multiutente; viene cancellato un utente, ma nei posti più strani del filesystem restano suoi file, e per la legge di Murphy occupano un sacco di posto.

Naturalmente, è possibile ricercare un utente o un gruppo specifico. I test sono -uid nn e -gid nn. Sfortunatamente non è possibile dare direttamente il nome dell'utente, ma bisogna usare l'id numerico, nn.

È possibile usare le forme +nn, che sta per ``un valore strettamente maggiore di nn'' e -nn, che sta per ``un valore strettamente minore di nn''; è piuttosto stupido nel caso degli UID, ma sarà comodo con altri test.

Un'altra opzione utile è -type c, che restituisce il valore vero se il file è di tipo c. Le corrispondenze mnemoniche per le scelte possibile sono le stesse che in ls; quindi si ha b per i file block special, c per quelli character special, d per le directory, p per le pipe con nome, l per i link simbolici, e s per le socket. I file regolari sono indicati con una f. Un test correlato è -xtype, che è simile a -type tranne nel caso dei link simbolici. Se non è stato dato -follow, il file a cui si punta viene controllato al posto del link stesso. Completamente scorrelato è il test -fstype tipo; in questo caso viene controllato il filesystem. Credo di aver preso questa informazione dal file /etc/mtab, quello che indica i filesystem che vengono montati; sono sicuro che i tipi nfs, tmp, msdos ed ext2 vengono riconosciuti.

I test -inum nn e -links nn controllano se il file ha numero di inode nn o nn link, mentre -size nn rende vero se il file ha allocato nn blocchi da 512 byte (beh, non precisamente: per i file sparsi i blocchi non allocati vengono contati lo stesso). Dato che al giorno d'oggi i risultati di ls -s non vengono sempre misurati in parti da 512 byte (Linux per esempio usa unità di 1K), è possibile appendere a nn il carattere b per contare in byte, o k per contare in kilobyte.

I bit di permesso vengono controllati con il test -perm modalità. Se modalità non ha segno, i bit di permesso dei file devono combaciare perfettamente. Un - che precede i bit significa che tutti i bit di permesso devono essere impostati, ma non fa assunzioni sugli altri; un + è soddisfatto se uno qualsiasi dei bit è impostato. Ops! Mi dimenticavo di dire che la modalità è scritta in ottale o simbolicamente, come si fa in chmod.

Il prossimo gruppo di test è correlato all'ora di ultimo utilizzo del file; è comodo quando un utente ha riempito il suo spazio, e come al solito ci sono moltissimi file inutilizzati da anni, e di cui si è dimenticato il significato. Il problema è trovarli, e find è l'unica speranza. -atime nn rende vero se il file è stato utilizzato nn giorni fa - ad esempio, con un comando chmod - e -mtime nn se il file è stato modificato per l'ultima volta nn giorni fa. Talvolta si ha bisogno di un tempo più preciso; il test newer file è soddisfatto se il file considerato è stato modificato dopo file. Quindi, dovete semplicemente usare touch con la data desiderata, ed avete fatto. Il find della GNU aggiunge i test -anewer e -cnewer che si comportano in maniera simile, ed i test -amin, -cmin e -mmin, che contano il tempo in minuti invece che in periodi di 24 ore.

Per ultimo, il test che uso più spesso: -name pattern rende vero se il nome del file corrisponde esattamente a pattern, che è più o meno quello che si usa in un ls standard. Perché `più o meno'? Perché naturalmente dovete ricordare che tutti i parametri sono processati dalla shell, e quei bei metacaratteri vengono espansi. Quindi, un test come -name foo* non renderà quello che volete, e dovreste scrivere o -name foo\* o -name "foo*". Questo è probabilmente uno degli errori più comuni fatti dagli utenti non attenti, quindi scrivetelo a lettere GRANDI sul vostro schermo. Un altro problema è che, come con ls, i punti all'inizio non vengono riconosciuti; per questo potete usare il test -path pattern, che non si preoccupa dei punti e delle barre quando paragona il percorso del file considerato con pattern.

Azioni

Ho detto che le azioni fanno, appunto, un'azione. Beh, -prune invece non fa qualcosa, cioè non discende all'interno dell'albero delle directory (a meno che non si dia -depth). Di solito si trova insieme a -fstype, per scegliere tra i vari filesystem da controllare.

Le altre azioni possono essere divise in due grandi categorie:

Operatori

Esistono numerosi operatori: eccone una lista, in ordine di precedenza decrescente.

( espr )
 
 
forza l'ordine precedente. Le parentesi devono naturalmente essere quotate, dato che hanno un significato anche per la shell.

! espr
 
-not espr
 
 
cambia il valore vero/falso dell'espressione, cioè se espr è vero diventa falso. Il punto esclamativo non ha bisogno di un carattere di escape, dato che è seguito da uno spazio.

espr1 espr2
 
espr1 -a espr2
 
espr1 -and espr2
 
 
corrispondono tutti all'operazione logica E, che è implicita nel primo e nel secondo caso. espr2 non viene valutata, se espr1 è falsa.

espr1 -o espr2
 
espr1 -or espr2
 
 
corrisponde all'operazione logica O. espr2 non viene valutata, se espr1 è vera.

espr1 , espr2
 
 
è l'indicazione dell'elenco; vengono valutate sia espr1 che espr2, (e naturalmente anche tutti gli effetti secondari!) ed il valore finale dell'espressione è quello di espr2.

Esempi

Beh sì, find ha proprio troppe opzioni, lo so, ma ci sono un sacco di modi predefiniti di usarlo che vale la pena ricordare, dato che vengono usati molto spesso. Vediamone alcuni.

% find . -name foo\* -print
trova tutti i nomi di file che cominciano per foo. Se la stringa può trovarsi all'interno del nome, probabilmente è più indicato scrivere "*foo*" invece di \*foo\*.

% find /usr/include -xtype f -exec grep foobar \
           /dev/null {} \;
è un grep eseguito ricorsivamente a partire dalla directory /usr/include. In questo caso ci interessano sia il file regolare che i link simbolici che puntano a file regolari, da cui il test -xtype. Molte volte è più semplice evitare di specificarlo, specialmente se siamo piuttosto sicuri che non esistono file binary che contengono la stringa desiderata. E perché il /dev/null nel comando? È un trucco per forzare grep a scrivere il nome del file dove trova una corrispondenza. Il comando grep viene applicato a ciascun file in un'invocazione diversa, e quindi non pensa che sia necessario di dare come output il nome del file; ma ora ci sono due file, cioè il file corrente e /dev/null! Un'altra possibilità è fare una pipe del comando verso xargs e fargli fare il grep. L'ho appena provato ed ho distrutto del tutto il mio filesystem (anche queste note che sto cercando di recuperare a mano :-( ).

% find /  -atime +1 -fstype ext2 -name core \
              -exec rm {} \;
è un tipico lavoro da crontab: cancella tutti i file core nei filesystem di tipo ext2 che non hanno avuto accessi nelle ultime 24 ore. È possibile che qualcuno voglia usare il file core per fare un dump post mortem, ma nessuno si può ricordare quello che stava facendo 24 ore prima...

% find /home -xdev -size +500k -ls > piggies
è utile sapere chi ha questi file che intasano il filesystem. Notate l'uso di -xdev: dato che ci interessa un solo filesystem, non è necesario scendere negli altri montati sotto /home.

Un'ultima parola

Tenete a mente che find è un comando che prende moltissimo tempo, dato che deve accedere a tutti gli inode del sistema per operare. È quindi saggio combinare tutte le operazioni che vi servono in un'unica invocazione di find, specialmente nei lavori di 'pulizia' fatti da crontab. Un esempio illuminante è questo: supponiamo di voler cancellare i file che finiscono con .BAK e cambiare le protezioni di tutte le directory a 771 e quelle dei file che finiscono con .sh a 755. E magari stiamo montando dei filesystem via NFS su un collegamento via modem, e non vogliamo controllare quei file. Perché scrivere tre comandi separati? Il modo più efficace di portare a termine il compito è questo:

% find . \( -fstype nfs -prune \) -o \
         \( -type d       -a -exec chmod 771 {} \; \) -o \
         \( -name "*.BAK" -a -exec /bin/rm {}   \; \) -o \
         \( -name "*.sh"  -a -exec chmod 755 {} \; \)

Sembra brutto (e che spreco di barre rovesciate!), ma guardando attentamente rivela che la logica che c'è sotto è molto lineare. Ricordate che quello che fa in realtà è una valutazione vero/falso; il comando compreso è solo un effetto secondario. Ma ciò significa che viene fatto solo se find deve valutare la parte exec dell'espressione, cioè solo se il lato sinistro della sottoespressione rende vero. Quindi, se per esempio il file considerato al momento è una directory viene valutato il primo exec ed i permessi dell'inode vengono cambiati a 771; altrimenti se ne dimentica e passa alla seconda sottoespressione. Probabilmente è più semplice vederlo in pratica che scriverlo, ma dopo un po' diventerà una cosa naturale.


next up previous contents index
Next: tarl'archiviatore a nastro Up: Comandi buffi Previous: Comandi buffi


Fri Sep 19 00:41:53 CEST 1997