Lanciare un comando su tutti i file con un certo nome
Dovete cambiare i diritti di accesso a tutti i file conuna certa estensione, oppure eliminare tutti i file di backup sparsi in giro per il filesystem, o concatenare tutti i file il cui nome segue una certa regola ma che stanno in directory diverse. Sono casi che si presentano spesso, e grazie alla shell, c'è un modo semplice ed efficace per eseguire questi compiti, all'apparenza diffici.
Usare find per cercare un file
In ognuno di quei casi, quello che vogliamo fare è eseguire uno stesso comando su un gruppo di file, identificabili da una caratteristica comune. Il primo step quindi è quello di riuscire a selezionarli.
Il comando find
è fatto appositamente per questo: consente di trovare (da cui il nome find) tutti i file nel filesystem date certe regole.
Ad esempio con
$ find ./ -name readme.md
otteniamo un elenco di tutti i file o le directory chiamate readme.md nel percorso corrente e in tutte le sottodirectory.
Adesso basterebbe poter eseguire un comando su ognuno di quei file.
Lo switch -exec
Lo switch -exec di find
permette di eseguire un comando su ognuno dei file individuati da find
.
Partiamo con un esempio:
$ find ./ -name readme.md -exec cat {} +
Questo comando esegue cat
su tutti i file chiamati readme.md nel percorso corrente e in tutte le directory sottostanti.
Analiziamo la sintassi di quanto passato a -exec: in pratica troviamo il comando da eseguire cat
, il gruppo parentesi graffa aperta + parentesi graffa chiusa che rappresenta il nome completo del file identificato da find
e il carattere + che segnala al comando la fine del omando da eseguire.
Esiste anche una sintassi alternativa in cui il + è sostituito da ; ma in questo caso occorre far precedere il ; da un backslash in modo che la shell non lo interpreti come separatore da un comando successivo:
$ find ./ -name readme.md -exec cat {} \;
Uso dei wildcards
Supponiamo di voler individuare tutti i file con una certa estensione in un'alberatura di percorsi, e magari di voler eseguire un certo comando su tutti questi files, sarebbe comodo poter usare dei wildcards per poterli identificare. Ovviamente find
permette di farlo ed in modo molto comodo.
Vediamo un esempio: supponiamo di voler eliminare tutti i file con estensione .bak da tutte le sottodirectory della directory corrente. Il comando da usare è:
$ find ./ -name '*.bak' -exec rm {} +
Si noti l'utilizzo degli apici singoli intorno a *.bak
per impedire alla shell di sostituire questa parte del comando con effettivi nomi di file .bak presenti nella directory corrente.
Altre idee e suggerimenti
Il comando find è molto versatile e potente, ha molti switch che si possono usare per adattarne il funzionamento a tanti casi diversi. Il suggerimento qui è quello di studiarne la pagina man
per farsi un'idea di tutto quello che può fare e magari ricordarsene nel momento del bisogno!
Voglio però lasciarvi alcuni esempi che a volte ho trovato utili:
find /etc -type d
Elenca tutte e sole le sottodirectory di /etc
find /etc -type l
Elenca solo i link simbolici nelle sottodirectory di /etc
find /etc -mtime 15
Elenca i file di /etc che sono stati modificati negli ultimi 15 giorni (intesi come 24*15 ore da adesso)
find /etc -cnewer /etc/hosts
Elenca i file di /etc che sono stati modificati successivamente a /etc/hosts
find /etc -type f -executable
Elenca i soli file nelle sottocartelle di /etc che l'utente corrente può eseguire
find /etc -type f -empty
Elenca solo i file vuoti nelle sottocartelle di /etc