mercredi 25 février 2009

Comment parcourir récursivement un répertoire pour appliquer des traitement en C

Lorsque l'on veut appliquer un traitement à des fichiers en parcourant l'arborescence. Il y a la possibilité d'utiliser la commande find associé à - exec.

Voici quelques exemples du monde réel world FIND usage


Copie dans un répertoire
find / -type f -name *.jpg -exec cp {} . \;
find /home/hudson/repertoire-data/ -type f -regex ".*ARTICLE_O.*" -exec echo {} \;
find /home/hudson/repertoire-data/ -type f -regex ".*ARTICLE_[^O].*" -exec echo {} \;
find . -type f -size +10000 -exec ls -al {} \;
Récherche des fichiers accédés il y a un jour
find . -atime +1 -type f -exec mv {} TMP \;
find . -name "-F" -exec rm {} \; # a script error created a file called -F
find . -exec grep -i "vds admin" {} \;
find . \! -name "*.Z" -exec compress -f {} \;
find . -type f \! -name "*.Z" \! -name ".comment" -print | tee -a /tmp/list
find . -exec chmod 775 {} \;
find . -user xuser1 -exec chown -R user2 {} \;
find . -exec grep PW0 {} \;
find . -exec grep -i "pw0" {} \;
find . -atime +6
find . -atime +6 -exec ll | more
find . -atime +6 -exec ll | more \;
find . -name auth*
find . -exec grep -i plotme10 {} \;
find . -ls -exec grep 'PLOT_FORMAT 22' {} \;
find . -print -exec grep 'PLOT_FORMAT 22' {} \;
find . -print -exec grep 'PLOT_FORMAT' {} \;
find . -print -exec grep 'PLOT_FORMAT' {} \;
find ./machbook -exec chown 184 {} \;
find . \! -name '*.Z' -exec compress {} \;
find . \! -name "*.Z" -exec compress -f {} \;
find /raid/03c/ecn -xdev -type f -print
find /raid/03c/ecn -xdev -path -type f -print
find / -name .ssh* -print | tee -a ssh-stuff
find . -name "*font*"
find . -name hpmcad*
find . -name *fnt*
find . -name hp_mcad* -print
find . -grep Pld {} \;
find . -exec grep Pld {} \;
find . -exec grep Pld {} \;
find . -exec grep PENWIDTH {} \; | more
find . -name config.pro
find /raid -type d -name ".local_sd_customize" -print
find /raid -type d -name ".local_sd_customize" -ok cp /raid/04d/MCAD-apps/I_Custom/SD_custom/site_sd_customize/user_filer_project_dirs {} \;
find /raid -type d -name ".local_sd_customize" -exec cp /raid/04d/MCAD-apps/I_Custom/SD_custom/site_sd_customize/user_filer_project_dirs {} \;
find . -name xeroxrelease
find . -exec grep xeroxrelease {} \;
find . -name xeroxrelease
find . -name xeroxrelease* -print 2>/dev/null
find . -name "*release*" 2>/dev/null
find / -name "*xerox*" 2>/dev/null
find . -exec grep -i xeroxrelease {} \;
find . -print -exec grep -i xeroxrelease {} \;
find . -print -exec grep -i xeroxrelease {} \; > xeroxrel.lis
find . -exec grep -i xeroxrel {} \;
find . -print -exec grep -i xeroxrel {} \;
find . -print -exec grep -i xeroxrel {} \; | more
find /raid/03c/inwork -xdev -type f -print >> /raid/04d/user_scripts/prt_list.tmp
find . -exec grep '31.53' {} \;
find . -ls -exec grep "31/.53" {} \; > this.lis
find . -print -exec grep "31/.53" {} \; > this.lis
find . -print -exec grep 31.53 {} \; > this.lis
find . -exec grep -i pen {} /;
find . -exec grep -i pen {} \;
find . -print -exec grep -i pen {} \; | more
find . -exec grep -i pen {} \;
find . -atime +6 -exec ll | more \;
find . -atime +6 -exec ll \;
find . -atime +6 -exec ls \;
find . -atime +30 -exec ls \;
find . -atime +30 -exec ls \; | wc -l
find . \! -name '*.Z' -exec compress -f {} \;
find . -name 'cache*' -depth -exec rm {} \;
find . -name 'cache*' -depth -print | tee -a /tmp/cachefiles
find . -name 'cache[0-9][0-9]*' -depth -print | tee -a /tmp/cachefiles
find . -name 'hp_catfile' 'hp_catlock' -depth -print | tee -a /tmp/hp.cats
find . -name 'hp_catfile' -name 'hp_catlock' -depth -print | tee -a /tmp/hp.cats
find . -name 'hp_cat*' -depth -print | tee -a /tmp/hp.cats
find . -name 'hp_cat[fl]*' -depth -print | tee -a /tmp/hp.cats
find /raid -name 'hp_cat[fl]*' -depth -print
find . \! -name '*.Z' -exec compress -f {} \;
find . -name '*' -exec compress -f {} \;
find . -xdev -name "wshp1*" -print
find . -xdev -name "wagoneer*" -print
find . -name "xcmd" -depth -print
find /usr/contrib/src -name "xcmd" -depth -print
find /raid -type d -name ".local_sd_customize" -exec ls {} \;
find /raid -type d -name ".local_sd_customize" \
-exec cp /raid/04d/MCAD-apps/I_Custom/SD_custom/site_sd_customize/user_filer_project_dirs {} \;



L'inconvénient de la méthode find est qu'elle passe par l'interpréteur de commande pour lancer le shell ce qui peut-être problématique lorsque l'on souhaite traiter des fichiers ayant des noms comportant des espaces, guillemets, parenthèse etc.

La seconde solution est de faire un programme C pour appliquer son traitement. La librairie ftw adresse spécialement ce type de problème, elle fonctionne comme la commande find.

Ses paramètres sont
* Le chemin de départ de la récursion
* La méthode à appeler pour chaque élement (Le nom peut varier mais elle doit retourner un entier et avoir trois arguments.
int nomdemethode(const char *path, const struct stat *ptr, int flag))

Voici un exemple :

#include <ftw.h>
#include <stdio.h>

int process(const char *path, const struct stat *ptr, int flag)
{
printf("Found\n path:%s\nflag%d\n", path, flag);
return 0;
}

int main(int argc, char *argv[]) {
if(argc == 1){
ftw(".", process, 1);
}
else{
ftw(argv[1], process, 1);
}
return 0;
}


Exemple d'utilisation :

Je voulais appliquer un traitement audio à de nombreux fichiers par le biais de la commande sox, malheureusement ce programme n'avait pas de fonction qui permettait d'effectuer des traitements par lots.

Les noms de fichiers comportaient aussi de nombreux caractères spéciaux qui empêchaient l'utilisation de la commande find. J'ai adapté le source du programme.

- Renommer la méthode main de sox par une méthode ancienmain
- Utiliser la méthode main du type présenté plus haut
- Créer une méthode appelée à chaque fois que ftw rencontre un nouvel élément on l'appelle process.

int process(char *dirname, const struct stat *status, int type){
if(!(type == FTW_F && strstr(dirname,".mp3")!=NULL&& strstr(dirname,"/L-")==NULL)){
return 0;
}

// Ici nous construisons les paramètres qui servent pour appeler l'ancien main.


char * params[6];
char nom_transforme[200];
struct stat s;
nom_transforme[0]=0;
params[0]="sox";
params[1]="-c1";
// ...
params[4]="remix";
params[5]="1";

/*
Les programmes ne sont généralement pas fait pour fonctionner en batch
aussi, il est nécessaire d'initialiser les variable globale à chaque appel.
Ce sont celle qui sont en dehors de toute méthode.
**/
mavarglobale1=0;
mavarglobale2=1;
...
mavarglobale3 = NULL;
mavarglobale4 = 0;

// Appel de l'ancien main
ancienmain(6,params);
}


Il ne s'agit clairement pas d'un controunement facile, et la volumétrie des fichiers doit clairement compenser l'effort de programmation par rapport à un appel de find.

1 commentaire:

Anonyme a dit…

J'ai cherché sur le web essayant de trouver des idées sur la façon d'obtenir mon blog personnel codées, votre style actuel et le thème sont merveilleux. Avez-vous code, il vous-même ou avez-vous recruté un codeur pour faire le travail pour vous personnellement?