Les commandes Find et Grep

Logo

Introduction

La commande find permet de retrouver des fichiers à partir de certains critères. Quelques exemples pratiques, notamment avec des expressions régulières, elles sont trop peu utilisées mais si puissantes.

La commande grep quant à elle permet d’explorer un ou une série de fichiers d’un même répertoire à la recherche de texte satisfaisant une expression régulière donnée. grep est l’acronyme de "Globally search for a Regular Expression and Print matching lines".

La commande find et ses options

Syntaxe de la commande find

find répertoire critères

Quelques critères de recherche les plus courants :

-name recherche sur le nom du fichier
-perm recherche sur les droits d’accès du fichier
-user recherche sur le propriétaire du fichier
-group recherche sur le groupe auquel appartient au fichier
-type recherche sur le type (d=répertoire, l=lien, f=fichier normal)
-size recherche par taille de fichier
-mtime recherche par date de dernière modification du fichier
-ctime recherche par date de création du fichier

Il est possible de combiner les critères avec des opérateurs logiques :

  • \( critere1 critere2 \) ou \( critere1 -a critere2 \) : ET logique (AND)
  • \( ! critere \) : NON logique (NOT)
  • \( critere1 -o critere2 \) : OU logique (OR)

La commande find est utilisée au moins avec l’option -print. Sans cette option minimale, même en cas de réussite dans la recherche, find n’affiche rien à la sortie standard.

La commande find est récursive, les répertoires et les sous répertoires sont scrutés par cette commande.

Recherche par nom de fichier

Option -name

Rechercher tous les fichiers se terminant par .c dans le répertoire /usr :

find /usr -name *.c -print
          /usr/share/bison/yacc.c
/usr/share/bison/glr.c
…

Rechercher dans le répertoire courant les fichiers avec l’extension .jpg ou l’extension .gif mais ne contenant pas le mot gimp dans le nom :

find . \( ! -name "*gimp*" -a \( -name "*.jpg" -o -name "*.gif" \) \) -print | sort
          …
./images/conception-html-dynamique-suppression-document.write-01.jpg
./images/conception-html-equations-math-mathjax-asciimath-01.jpg
./images/google-analytics-optimisation-mesure-audience-01.jpg
./images/google-analytics-optimisation-mesure-audience-02.jpg
…

Par défaut les résultats ne sont pas triés, c’est pourquoi ci-dessus sort est appelé sur la sortie de la commande find.

Pour spécifier le répertoire courant : find . < critères >

Recherche par dates

Option -mtime +/-

Rechercher les fichiers *.js ou *.css modifiés il y a moins de 2 jours :

find . -mtime -2 -a \( -name "*.js" -o -name "*.css" \) -print
          ./css/style-df.css
./css/style.css
./js/resources/nohttp.js

Option -ctime +/-

Rechercher les fichiers *.json créés il y a plus de 30 jours dans le répertoire $LOG :

find $LOG -ctime +30 -name "*.json" | sort 
          ./postgresql-9.6-setup-installation-rapide_20200929030800.json
./postgresql-9.6-setup-installation-rapide_20200929023720.json
…

L’option -mtime -2 est en fait équivalente à -48h, l’option -ctime +30 à +30 × 24h : find se base par défaut sur la date et l’heure courante. Utiliser daystart pour se baser réellement sur le nombre de jours sans prendre en compte l’heure courante.

find . -daystart -mtime -2 -name "*.html"

Recherche suivant la taille

Option -size +/-

Identifier dans une arborescence les fichiers *.html dont la taille est supérieure à 50K (soit 100 blocs de 512o) :

find . -size +100 -name "*.html" -print
          ./influxdb-v1.7-architecture-installation-configuration-utilisation.html
./sybase-ase-iq-comparaison.html
…

Dans la pratique, l’unité (k | M | G) est spécifiée pour ne pas calculer les multiples de 512 octets.

find . -size +50k -name "*.html" -print
find . -size +100M -print
find . -size +2G -print

Redirection des messages d’erreur

Les droits d’accès ne sont pas toujours autorisés pour certains répertoires, par conséquent, la commande find peut générer un grand nombre de messages d’erreur (permission denied, etc.). Pour éviter ceci, rediriger les messages d’erreur vers un fichier "poubelle" (ex /dev/null), les messages d’erreur sont alors perdus. Il est toutefois possible de sauvegarder ces erreurs dans un fichier régulier.

Exemple :

find . \( -name a.out -o -name "*.c" \) -print > /dev/null

find et l’option exec

L’option -print est une option que l’on passe à la commande find pour afficher les résultats à la sortie standard. L’option -exec est disponible dans la commande find et elle est exclusive de l’option -print.

Lorsque la commande find est couplée à l’option exec, il est alors possible d’exécuter une commande sur les fichiers trouvés par la commande find.

find repertoire critères -exec commande {} \;

La sortie de la commande find avec l’option -print est très basique, voire trop :

find . -type f -size +100k -print
          ./sybase-replication-server-guide-pratique.pdf
./images/gimp-supprimer-couleur-arriere-plan-fond-09.jpg
…

Avec l’option -exec, la commande find est usuellement combinée avec la commande ls pour afficher plus de détails sur les résultats :

find . -type f -size +100k -exec ls -lh {} \; 2> /dev/null
          -rw-r--r-- 1 sqlpac wapp 118K Jun 15 11:34 ./sybase-replication-server-guide-pratique.pdf
-rw-r--r-- 1 sqlpac wapp 104K Jun 15 11:33 ./images/gimp-supprimer-couleur-arriere-plan-fond-09.jpg
…

Autres exemples courants :

Supprimer tous les fichiers core avec la commande rm :

find . -name core -exec rm {} \;

Supprimer tous les fichiers *.json créés il y a plus de 10 jours dans le répertoire $LOG avec la commande rm :

find $LOG -name "*.json" -ctime +10 -exec rm {} \;

Des exemples encore plus concrets :

On recherche dans un répertoire et ses sous répertoires la liste des fichiers *.htm, *.html, *.inc, *.php, *.css, *.json, *.xml pour lesquels l’encodage est iso-8859-1. L’encodage est donné par la commande file et l’option -i :

find . -type f  \( -name "*.html" -o -name "*.htm" -o -name "*.json" -o -name "*.php" -o -name "*.inc" -o -name "*.x
ml" -o -name "*.css" -o -name "*.xml" \) -exec file -i {} \;  | grep -i 'iso-8859-1'
          ./admpmgportal/config.inc: text/x-php; charset=iso-8859-1
./admpmgportal/include/rules.inc: text/x-php; charset=iso-8859-1
./admpmgportal/include/treeview.inc: text/html; charset=iso-8859-1...

Pour retrouver tous les fichiers non binaires contenant la chaîne de caractères '79.13' :

find . -type f -exec grep -Il '79\.13' {} \;
          ./redis/dba/srvrdisqlpac/cfg/srvrdisqlpac.conf
...

Bien utile pour rechercher des codages (adresses IP, fonctions…) dans une arborescence, quel que soit le type de fichier du moment qu’il ne s’agisse pas d’un fichier binaire. L’option -I dans la commande grep écarte les fichiers binaires.

La commande find et les expressions régulières (-regex et -regextype)

Option -regex

L’exemple précédent n’est pas très élégant ( -o -name "*.css" -o -name "*.php… ). Les expressions régulières sont implémentées dans la commande find avec l’option -regex.

Le code devient bien plus lisible avec cette fonctionnalité.

find . -regex '.*\.\(css\|htm\|html\|inc\|js\|json\|php\|xml\)' -exec file -i {} \;

Plusieurs librairies existent pour les expressions régulières (posix, GNU awk…), librairies pour lesquelles les syntaxes des expressions régulières peuvent différer.

L’option -regextype donne la librairie à utiliser pour l’expression régulière : un exemple de recherche des fichiers *.txt et *.inc avec la librairie posix-basic.

find -regextype posix-basic -regex ".*\(txt\|inc\)" -print

Les librairies et syntaxes des expressions régulières sont nombreuses et ne sont pas détaillées ici, ce n’est pas l’objet de cet article.

Une petite astuce pour retrouver les librairies disponibles sur la plateforme utilisée : appeler une commande find avec une option -regextype invalide.

find .-regextype dummy
          find: Unknown regular expression type `dummy'; valid types are `findutils-default', `awk', `egrep', `ed',
`emacs', `gnu-awk', `grep', `posix-awk', `posix-basic', `posix-egrep',
`posix-extended', `posix-minimal-basic', `sed'.

Pour gérer l’insensibilité à la casse dans les expressions régulières, utiliser option -iregex.

find -iregex ".*\.\(txt\)' -print./README.TXT

Combinaison des commandes find et xargs

Cette fois, on recherche l’appel de la fonction PHP ereg_replace dans le code source des fichiers *.php et *.inc.

find . -type f  \( -name "*.php" -o -name "*.inc" \) -print | xargs grep -ni "ereg_replace"
          ...
./sqlpacv2/prp_article.php5:216:    $caption = ereg_replace("\.","",_USRDIR_DOC)."/".$datadoc[1]["fichier"];
./sqlpacv2/prp_glossaire.php5:95:   $caption = ereg_replace("\.","",__USRDIR_DOC)."/".$article["fichier"];

La commande xargs exécute une commande echo/cat sur le fichier retourné par l’option -print de la commande find. Ainsi la commande qui suit xargs (grep dans l’exemple ci-dessus) est exécutée sur le fichier.

Find et les liens

Option -xtype

La commande find est très pratique pour retrouver les liens "cassés" (broken links) :

find . -xtype l -exec ls -ll {} \;
          lrwxrwxrwx 1 sqlpac wapp 26 Sep  3 14:33 ./postmenu.php -> ../../engines/postmenu.php

Si l’option -xtype n’est pas disponible sur la plateforme utilisée, utiliser la commande test :

find . -type l  ! -exec test -e {} \; -exec ls -ll {} \;
lrwxrwxrwx 1 sqlpac wapp 26 Sep  3 14:33 ./postmenu.php -> ../../engines/postmenu.php

Un exemple bien utile : calculer la taille des images dans un répertoire

En une ligne de commande en combinant find et awk, pour calculer la taille des images dans un répertoire :

find . -regex '.*\.\(png\|gif\|jpg\|jpeg\)' -exec ls -l {} \; | \
          awk 'BEGIN {sum=0} {sum+=$5} END { printf("%.2f %s\n",sum/1024000,"Mb") }'
          18.16 Mb
          

La commande grep et ses options

Syntaxe de la commande grep

grep -option(s) expression fichier(s)

Quelques options usuelles :

-v affiche les lignes ne satisfaisant pas l’expression
-c compte le nombre de lignes satisfaisant l’expression sans afficher les lignes
-n affiche la ligne et le numéro de la ligne satisfaisant l’expression
-i ignorer la casse

Some examples :

Rechercher des tableaux (balises HTML <table>) dans les fichiers *.html :

grep "<table" *.html
          …
sybase-iq-12.7-migration-ase-vers-iq.html:          <table class="alt- r-brdr rco-">
sybase-iq-12.7-migration-ase-vers-iq.html:          <table>
sybase-iq-12.7-migration-ase-vers-iq.html:          <table class="alt- r-brdr">
…

Avec les numéros de lignes et sans sensibilité à la casse :

grep -ni "<table" *.html
          …
sybase-iq-12.7-migration-ase-vers-iq.html:232:          <table class="alt- r-brdr rco-">
sybase-iq-12.7-migration-ase-vers-iq.html:512:          <table>
sybase-iq-12.7-migration-ase-vers-iq.html:661:          <table class="alt- r-brdr">
…

Juste le nombre d’occurences en ignorant la casse :

grep -ci "<table" *.html
          …
sybase-iq-12.7-migration-ase-vers-iq.html:6
…
sybase-iq-index-cardinalite-sp_dba_helpcolumn.html:0
…

Les résultats en sortie de la commande grep sont séparés par :, bien pratique pour traiter rapidement les résultats avec l’utilitaire awk.

grep -ci "<table" *.html | \
          awk -F":" 'BEGIN { hf=0; tb=0; } { if ($2 != 0) { hf++; tb +=$2 } } END { print tb" tableaux dans "hf" fichiers"}'
          549 tableaux dans 187 fichiers

Les expressions régulières avec grep

L’option -E donne une expression régulière à la commande grep.

Pour rechercher la chaîne de caractères mysql_connect, mysql_query et mysql_close dans des fichiers php avec les numéros de ligne :

grep -ni -E "mysql_connect|mysql_query|mysql_close" *.php
          cls_database_myisam.php5:59:    $objRessource = mysql_connect(_SGBD_SERVER,_SGBD_USER,$pwdUnCrypted);
cls_database_myisam.php:81:             $ret=mysql_close($objRessource);
cls_database_myisam.php5:108:   $return = mysql_query($queryString,$objRessource);
cls_database_myisam.php5:114:   $reserrors = mysql_query('select @errno as errno , @errmsg as errmsg'
cls_database_myisam.php5:341:   $resultPlan=mysql_query("EXPLAIN ".$query,$objRessource);

La commande egrep n’est ni plus ni moins que la commande grep avec l’option -E :

egrep -ni "mysql_connect|mysql_query|mysql_close" *.php

Une autre syntaxe possible plus élégante :

egrep -ni "mysql_(connect|query|close)" *.php

La liste des termes de l’expression régulière est parfois longue pour une seule ligne de commande, c’est pourquoi les termes peuvent être définis dans un fichier texte, fichier donné à la commande grep avec l’option -f.

grep -ni -f regex.txt *.php
regex.txt
mysql_connect
mysql_query

Comme pour la commande find, la commande grep autorise l’utilisation de différentes syntaxes d’expressions régulières.

  • Option -E : expressions régulières étendues (ERE, Extended Regular Expressions)
  • Option -G : expressions régulières basiques (BRE, Basic Regular Expressions)
  • Option -P : expressions régulières PERL (PRE, Perl Regular Expressions)

Quelques autres exemples :

Rechercher des lignes commençant par une étoile, le caractère ^ dans l’expression régulière signifiant "commençant par" :

grep -ni -E "^ \*" *.php

Rechercher des lignes finissant par un point virgule , le caractère $ dans l’expression régulière correspondant à une fin de ligne :

grep -ni -E "$;" *.php

En reprenant les premiers exemples, rechercher tous les tableaux dans les fichiers *.html ayant la classe CSS rco- et/ou r-brdr :

egrep -ni "<table.*class.*(rco-|r-brdr).*>" *.html
        …
sybase-iq-12.7-migration-ase-vers-iq.html:53:          <table class="alt- r-brdr rco-">
sybase-iq-12.7-migration-ase-vers-iq.html:121:          <table class="alt- r-brdr rco-">
sybase-iq-12.7-migration-ase-vers-iq.html:152:          <table class="alt- r-brdr rco-">
sybase-iq-12.7-migration-ase-vers-iq.html:232:          <table class="alt- r-brdr rco-">
sybase-iq-12.7-migration-ase-vers-iq.html:661:          <table class="alt- r-brdr">
…

Lister les fichiers *.html ne contenant pas de tableaux (option -L) :

grep -L "<table" *.html