find est un utilitaire UNIX de longue date. Son objectif est de parcourir de façon récursive un ou plusieurs répertoires et d'y trouver des fichiers correspondant à un certain ensemble de critères. Bien qu'il soit très utile, sa syntaxe est vraiment complexe, et l'utiliser requiert une certaine pratique. La syntaxe générale est :
find [options] [répertoires] [critère1]... [critèreN] [action]
Si vous ne spécifiez aucun répertoire, find recherchera dans le répertoire courant. L'absence de critère équivaut à « vrai », et donc tous les fichiers seront trouvés. Les options, les actions et les critères possibles sont si nombreux que nous n'en mentionnerons que quelques-uns. Commençons par les options :
-xdev
: ne pas
étendre la recherche aux répertoires se trouvant sur d'autres
systèmes de fichiers.
-mindepth
<n>
: descendre d'au moins
n
niveaux au-dessous du répertoire de
recherche avant de chercher des fichiers.
-maxdepth
<n>
: rechercher les fichiers se trouvant au
plus n
niveaux au-dessous du répertoire de
recherche.
-follow
:
suivre les liens symboliques s'il pointent vers des
répertoires. Par défaut, find ne les suivra
pas.
-daystart
:
quand il est fait usage de tests relatifs à la date et à l'heure (voir
ci-dessous), prendre le début de la journée courante comme repère au
lieu de la marque par défaut (24 heures avant l'heure
courante).
Un critère peut être un ou plusieurs tests atomiques; quelques tests utiles sont :
-type
<type_de_fichier>
: rechercher un type de
fichiers donné ; type_de_fichier
peut
être : f
(fichier normal),
d
(répertoire), l
(lien
symbolique), s
(socket ou interface de
connexion), b
(fichier en mode bloc),
c
(fichier en mode caractère) ou
p
(tube nommé).
-name
<motif>
: trouver les fichiers dont les noms
correspondent au motif donné. Avec cette option, le motif est
traité comme un motif
d'englobement du shell (voir Section 3, « Motifs d'englobement du shell »).
-iname
<motif>
: comme -name
, mais
ne tient pas compte de la casse.
-atime
<n>
, -amin <n>
:
trouver les fichiers dont la dernière date d'accès remonte à
n
jours (-atime
) ou
n
minutes (-amin
). Vous
pouvez aussi spécifier +<n>
ou
-<n>
, auquel cas la recherche sera
effectuée pour des fichiers dont la date d'accès remonte à au
plus ou au moins n
jours/minutes.
-anewer
<fichier>
: trouver les fichiers auxquels on
a accédé plus récemment que fichier
.
-ctime <n>
,
-cmin <n>
, -cnewer
<fichier>
: même chose que pour
-atime
, -amin
et
-anewer
, mais s'applique à la dernière date de
changement du contenu des fichiers.
-regex
<motif>
: comme pour -name
, mais
motif
est traité comme une expression régulière.
-iregex
<motif>
: comme -regex
, mais sans
tenir compte de la casse.
Beaucoup d'autres tests existent, référez-vous à find(1) pour plus de détails. Pour combiner ces tests, vous pouvez utiliser :
<c1> -a
<c2>
: vrai si c1
et
c2
sont tous les deux vrais ;
-a
est implicite, donc vous pouvez utiliser
<c1> <c2> <c3>
si vous voulez
que tous les tests c1
, c2
et c3
concordent.
<c1> -o
<c2>
: vrai si l'un de c1
ou c2
est vrai, ou les deux. Notez que
-o
a une priorité moins grande que
-a
, donc si vous voulez que les fichiers
correspondent aux critères c1
ou
c2
et qu'ils correspondent également au
critère c3
, vous devrez utiliser des
parenthèses et écrire ( <c1> -o <c2> ) -a
<c3>
. Vous devez échapper (désactiver) les
parenthèses, sans quoi le shell les prendra en compte dans
son interprétation !
-not
<c1>
: inverse le test
c1
, donc -not <c1>
est
vrai si c1
est faux.
Enfin, vous pouvez demander une action précise pour chaque fichier retrouvé. Les plus fréquentes sont :
-print
:
écrit seulement le nom de chaque fichier sur la sortie standard. C'est
l'action par défaut si vous n'en spécifiez aucune.
-ls
:
affiche l'équivalent d'un ls -ilds sur chaque
fichier trouvé sur la sortie standard.
-exec
<commande>
: exécute la commande
commande
sur chaque fichier trouvé. La ligne
de commande command
doit se terminer par un
;
, que vous devez désactiver de telle sorte
que le shell ne l'interprète pas : la position du
fichier dans la commande est repérée par
{}
. Regardez les exemples
d'utilisation pour bien comprendre.
-ok
<commande>
: même chose que -exec
mais demande confirmation pour chaque commande.
La meilleure façon de tout assimiler
ces options et paramètres est à travers d'autres
exemples. Supposons que vous désiriez trouver tous les répertoires
dans le répertoire /usr/share
. Tapez
alors :
find /usr/share -type d
Admettons que vous ayez un serveur
HTTP, que tous vos fichiers HTML soient dans
/var/www/html
, qui s'avère aussi être votre
répertoire courant. Il s'agit de chercher tous les fichiers qui n'ont
pas été modifiés depuis... un mois, par exemple. Les pages
proviennent, incidemment, de différents auteurs : certains
fichiers auront une extension html
et d'autres,
l'extension htm
. Vous voulez lier ces fichiers
dans le répertoire /var/www/obsolete
. Vous
taperez alors[30] :
find \( -name "*.htm" -o -name "*.html" \) -a -ctime -30 \ -exec ln {} /var/www/obsolete \;
D'accord, cette exemple est un peu compliqué et requiert quelques explications. Le critère est :
\( -name "*.htm" -o -name "*.html" \) -a -ctime -30
qui accomplit ce qu'on lui
demande : il recherche tous les fichiers dont le nom se
termine soit par .htm
, soit par
.html
(\( -name "*.htm" -o
-name "*.html" \)), et
(-a) qui n'ont pas été modifiés dans les
derniers 30 derniers jours, ce qui représente en gros un mois
(-ctime -30). Notez les parenthèses : elles
sont nécessaires ici, parce que -a a une
priorité plus grande. En leur absence, tous les fichiers se
terminant par .htm
auraient été sortis, plus
tous les fichiers se terminant par .html
n'ayant pas été modifiés depuis un mois, ce qui n'est pas ce que
nous voulons. Notez également que les parenthèses sont désactivées
par rapport au shell : si nous avions mis (
.. ) à la place de \( .. \), le
shell les aurait interprétés et aurait tenté d'exécuter
-name "*.htm" -o -name
"*.html" dans un sous-shell... Une
autre solution aurait été de mettre les parenthèses entre doubles
ou simples apostrophes, mais une barre oblique inverse
(backslash) est préférable ici dans
la mesure où nous ne devons isoler qu'un seul caractère.
Et enfin, la commande doit être exécutée pour chacun des fichiers :
-exec ln {} /home/httpd/obsolete \;
Ici aussi, vous devez désactiver le
;
par rapport au shell, car autrement
le shell l'interprétera comme un séparateur de
commandes. Si vous ne le faites pas, find se
plaindra qu'il manque un argument à
-exec
.
Un dernier exemple : vous avez un
gros répertoire nommé /shared/images
, contenant
toutes sortes d'images. Régulièrement, vous utilisez la commande
touch pour mettre à jour les dates d'un fichier
nommé stamp
dans ce répertoire, de façon à avoir
une référence dans le temps. Vous voulez trouver tous les fichiers
JPEG
dans ce répertoire qui sont plus récents que le fichier
stamp
, et comme vous avez des images de diverses
sources, ces fichiers ont des extensions jpg
,
jpeg
, JPG
ou
JPEG
. Vous voulez aussi éviter de rechercher dans
le répertoire old
. Vous voulez vous faire envoyer
la liste de ces fichiers par courrier électronique, et votre nom
d'utilisateur est pierre
:
find /shared/images -cnewer \ /shared/images/stamp \ -a -iregex ".*\.jpe?g" \ -a -not -regex ".*/old/.*" \ | mail pierre -s "Nouvelles images"
Bien sûr, cette commande n'est pas très utile si vous devez l'exécuter régulièrement, car vous devrez l'entrer à chaque fois. Il est possible de le faire ainsi :
[30] Notez que cet exemple
requiert que /var/www
et
/var/www/obsolete
soient sur le même système de
fichier !