[C] Afficher les fichiers selon leur date de modification

Le problème exposé dans ce sujet a été résolu.

Bonjour !

Je suis en train d’essayer de faire une fonction qui prend en paramètre un dossier de départ, un statut (0 ou 1, qui correspond à un moins de … ou à un plus de …) et une valeur. Elle doit ensuite parcourir tous les fichiers du dossier de départ et de tous ses sous-dossiers en affichant ceux qui ont une date de dernier accès >= ou <= (selon le statut) à la valeur. J’ai ce code pour le moment :

void rec_date(char *starting_point, int statut, int valeur) { // dossier +/rien valeur m/h/j
    /* Affiche récursivement tous les fichiers dont la dernière modification respecte la période */

    // Gestion ouverture fichier/dossier
    DIR *dp ;
    struct dirent *current ;
    struct stat s ;

    dp = opendir(starting_point) ;
    if (dp == NULL) {
        perror("opendir") ;
        exit(1) ;
    }

    // Parcours
    while (current = readdir(dp)) {

        if (strcmp(current->d_name, ".") == 0 || strcmp(current->d_name, "..") == 0)
            continue ; // Pour ne pas reparcourir tout le système de fichier

        if (current->d_type == DT_DIR) { // Sous-dossier

            // Actualisation chemin
            char chemin[BUFFER_CHEMIN] ;
            snprintf(chemin, sizeof(chemin), "%s/%s", starting_point, current->d_name) ;

            // Parcours récursif
            rec_date(chemin, statut, valeur) ;

        } else { // Fichier
            
            // Temps actuel = time(NULL)
            stat(current->d_name, &s) ;
            printf("%s %d\n", current->d_name, s.st_mtime) ;
            
            if ((statut == 0 && (time(NULL) - s.st_mtime) <= valeur) || (statut == 1 && (time(NULL) - s.st_mtime) >= valeur)) 
                printf("%s/%s\n", starting_point, current->d_name) ;

        }

    }

    // Fermeture du dossier
    closedir(dp) ;

}

Il fonctionne parfaitement pour les différentes dates dans le répertoire courant par contre, avec le printf("%s %d\n", current->d_name, s.st_mtime) ; de débugage, je me rends compte que la date s.st_mtime est identique pour tous les fichiers dans les sous-dossiers (et je ne sais pas quelle valeur la date prend, parfois c’est 0 pour tous parfois autre chose) donc l’affichage devient faux… Comment pourrais-je faire pour régler ce problème ? (et avoir un comportement quasiment identique à celui de ls -lRu)

Merci d’avance pour votre réponse ! :)

+0 -0

Salut,

Il fonctionne parfaitement pour les différentes dates dans le répertoire courant par contre, avec le printf("%s %d\n", current->d_name, s.st_mtime) ; de débugage, je me rends compte que la date s.st_mtime est identique pour tous les fichiers dans les sous-dossiers (et je ne sais pas quelle valeur la date prend, parfois c’est 0 pour tous parfois autre chose) donc l’affichage devient faux… Comment pourrais-je faire pour régler ce problème ? (et avoir un comportement quasiment identique à celui de ls -lRu)

Jupiter41

En fait, les trois champs st_atime, st_mtime et st_ctime sont des time_t et non des int. Si tu te réfères aux pages de manuels stat(2) et stat(3), tu y verras les définitions suivantes (pour la GNU libc) :

struct timespec  st_atim;  /* Time of last access */
struct timespec  st_mtim;  /* Time of last modification */
struct timespec  st_ctim;  /* Time of last status change */

#define st_atime  st_atim.tv_sec  /* Backward compatibility */
#define st_mtine  st_mtim.tv_sec
#define st_ctime  st_ctim.tv_sec

La structure timespec est quant à elle définie comme suit.

struct timespec {
	time_t          tv_sec;
	long            tv_nsec;
};

Or, le type time_t est très probablement un long ou un long long. Essaye plutôt de l’afficher comme suit. ;)

printf("%s %llu\n", current->d_name, (unsigned long long)s.st_mtime);

Sinon, petite remarque : la page de manuel de readdir(3) précise que le champ d_type ne supporte pas tous les systèmes de fichiers. Il est préférable de passer par la fonction stat() pour déterminer si une entrée est un répertoire ou un fichier. :)

+1 -0

Merci beaucoup pour ta réponse et tes précisions sur stat() !

Le problème c’est que dans ma condition time(NULL) - s.st_mtime <= valeur peu importe le type de s.st_mtime cela devrait passer :(

Voici un screen de mon arborescence de fichiers avec les dates de dernier accès (ls -lRu) : https://ibb.co/NxhKNq0

Pourtant, quand j’exécute ma commande, "./ftc . -date 10m", il ne m’affiche que ./ftc. Alors qu’il parcourt bien tous les fichiers/dossiers car quand je fais "./ftc . -date +5h" le programme affiche tous les fichiers dans les sous-dossiers, peu importe leur date de création (tous les fichiers dans src/ et Test_Récursivité/ sont affichés, alors que certains de leur dernier accès remonte à moins de 5h) (et sans afficher ./ftc qui est dans ., ce qui est normal) Je ne vois pas du tout pourquoi cela fait ça…

Voici tout mon programme si besoin : https://pastebin.com/TfDmxm73

+0 -0

Le problème c’est que dans ma condition time(NULL) - s.st_mtime <= valeur peu importe le type de s.st_mtime cela devrait passer :(

Jupiter41

Ah, oui, effectivement, au temps pour moi, je m’étais focalisé sur le printf() de débogage. ^^'

La première chose, je pense, c’est que tu devrais vérifier les valeurs que tu envoies à rec_date(), tu effectues pas mal d’opération sur argv et j’ai un doute sur ce que tu obtiens. Il y aurait moyen d’améliorer la lisibilité en récupérant le signe, la valeur et l’unité avec sscanf().

char signe[2], unite[2];
int valeur;

/*
 * +1h, -1j ou +2m sont récupérés sans soucis.
 */
if (sscanf(argv[3], "%1[+-]%d%1[mhj]", signe, &valeur, unite) == 3) {
    /* Le format est a priori bon. */
}

Note que le format du type %1[-+] spécifie une chaîne de caractères (d’où le tableau de taille 2, pour contenir le caractère nul) d’au plus un caractère composée exclusivement des caractères « + » et « - ». Note également que dans ce cas ci, si sscanf retourne 2 cela signifie qu’aucune unité n’a été spécifiée et que tu as un nombre de secondes.

Je t’invite déjà à simplifier cette partie et à vérifier que ce que tu obtiens est correct.

Le calcul final de la fonction rec_date() à l’air correct, cependant, l’appel à la fonction stat() est incorrect en cas de récursion.

stat(current->d_name, &s);

Tu n’auras que le nom du fichier et non son chemin complet avec current->d_name, ton appel va donc échouer. Vérifie également le retour de la fonction stat(), c’est important.

+0 -0

Merci pour ces conseils, je n’avais pas du tout pensé à sscanf pour récupérer les paramètres plus simplement !

Le programme marche un petit mieux, mais pas totalement (je ne sais pas pourquoi :( ) J’ai modifié la fonction récursive pour recalculer le chemin à chaque fois :

void rec_date(char *starting_point, int statut, int valeur) { // dossier +/rien valeur m/h/j
    /* Affiche récursivement tous les fichiers dont la dernière modification respecte la période */

    // Gestion ouverture fichier/dossier
    DIR *dp ;
    struct dirent *current ;
    struct stat s ;

    dp = opendir(starting_point) ;
    if (dp == NULL) {
        perror("opendir") ;
        exit(1) ;
    }

    // Parcours
    while (current = readdir(dp)) {

        // Actualisation chemin
        char chemin[BUFFER_CHEMIN] ;
        snprintf(chemin, sizeof(chemin), "%s/%s", starting_point, current->d_name) ;

        if (strcmp(current->d_name, ".") == 0 || strcmp(current->d_name, "..") == 0)
            continue ; // Pour ne pas reparcourir tout le système de fichier

        if (current->d_type == DT_DIR) { // Sous-dossier

            // Parcours récursif
            rec_date(chemin, statut, valeur) ;

        } else { // Fichier
            
            // Temps actuel = time(NULL)
            stat(chemin, &s) ;
            
            if ((statut == 0 && (time(NULL) - s.st_mtime) <= valeur) || (statut == 1 && (time(NULL) - s.st_mtime) >= valeur)) 
                printf("%s/%s\n", starting_point, current->d_name) ;

        }

    }

    // Fermeture du dossier
    closedir(dp) ;

}

Avec les fichiers : https://ibb.co/18KsBF0

Avec "./ftc . -date 10m", j’ai la sortie :

Arguments : 0 600 (pour print les arguments d’appel de rec_date, statut et valeur)

./ftc

./src/Date.c

Alors que tous les fichiers avec 17h53 dans src/ devrait s’afficher aussi…

+0 -0

Difficile à dire sans plus d’informations. Tu devrais vérifier le retour de la fonction stat() et afficher le résultat de la différence. :)

Testé sans compilation, il y a peut-être une ou deux coquilles, mais tu vois l’idée. ^^

void
rec_date(char *starting_point, int statut, int valeur) {
	DIR *dp;
	struct dirent *current;
	struct stat s;

	dp = opendir(starting_point);

	if (dp == NULL) {
		perror("opendir");
		exit(EXIT_FAILURE);
	}

	while ((current = readdir(dp)) != NULL) {
		char chemin[BUFFER_CHEMIN];
		snprintf(chemin, sizeof(chemin), "%s/%s", starting_point, current->d_name);

		if (strcmp(current->d_name, ".") == 0 || strcmp(current->d_name, "..") == 0)
			continue;

		if (current->d_type == DT_DIR)
			rec_date(chemin, statut, valeur);
		else {
			if (stat(chemin, &s) < 0) {
				perror("stat");
				exit(EXIT_FAILURE);
			}

			unsigned long long diff = (unsigned long long)time(NULL) - s.st_mtime;
			printf("%s/%s: %llu\n", starting_point, current->d_name, diff);

			if ((statut == 0 && diff <= valeur) || (statut == 1 && diff >= valeur)) 
				printf("%s/%s\n", starting_point, current->d_name);
		}
	}

	closedir(dp);
}
+2 -0

Merci beaucoup pour toutes tes réponses !

En fait il y avait 2 problèmes avec mon programme : le chemin qui n’était pas réactualisé à chaque fois (donc stat() n’était pas bon comme la fonction prend un chemin complet) et refaire des tests avec ton dernier message m’a fait me rendre compte d’une chose : j’utilisais st_mtime qui est la dernière modification au lieu de atime pour le dernier accès :-°

ça marche maintenant !

Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte