2010-04-09 8 views
1

J'essaie de simuler linux commande ls en utilisant linux api de c. En regardant le code, cela a du sens, mais quand je l'exécute, j'obtiens une erreur "stat: pas de fichier ou de répertoire". J'ai vérifié qu'opendir fonctionne bien. Je pense que le problème est dans stat, qui retourne -1 même si je pense qu'il devrait retourner 0.Liste des répertoires sous Linux à partir de C

Qu'est-ce qui me manque?

Merci pour votre aide.

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#include <dirent.h> 
#include <sys/stat.h> 
#include <errno.h> 

int main(int argc, char *argv[]) 
{ 
DIR *dirp; 
struct dirent *direntp; 
struct stat stat_buf; 
char *str; 

if (argc != 2) 
{ 
    fprintf(stderr, "Usage: %s dir_name\n", argv[0]); 
    exit(1); 
} 

if ((dirp = opendir(argv[1])) == NULL) 
{ 
    perror(argv[1]); 
    exit(2); 
} 

while ((direntp = readdir(dirp)) != NULL) 
{ 
    if (stat(direntp->d_name, &stat_buf)==-1) 
    { 
    perror("stat ERROR"); 
    exit(3); 
    } 
    if (S_ISREG(stat_buf.st_mode)) str = "regular"; 
    else if (S_ISDIR(stat_buf.st_mode)) str = "directory"; 
    else str = "other"; 
    printf("%-25s - %s\n", direntp->d_name, str); 
} 

closedir(dirp); 
exit(0); 
} 

Répondre

3

C'est parce que vous n'êtes pas énoncez le fichier réel. C'est dans un répertoire différent. Si vous voulez le vrai nom de fichier, combinez argv[1] et direntp->d_name avec un '/' entre eux.

De plus, le nommage hongrois est icky, même le bit mineur comme 'p' à la fin. Si vous avez tant de variables dont vous avez besoin de suivre leurs types dans leurs noms, vous faites quelque chose de mal.

Voici une version révisée de votre programme:

#include <stdio.h> 
#include <stdlib.h> 
#include <stddef.h> 
#include <string.h> 
#include <dirent.h> 
#include <sys/stat.h> 
#include <errno.h> 
#include <limits.h> 
#include <sys/param.h> 

int main(int argc, char *argv[]) 
{ 
DIR *dirp; 
struct dirent *direntp; 
struct stat stat_buf; 
char *str; 
char fullpath[MAXPATHLEN + 1]; 
size_t dirnamelen; 

if (argc != 2) 
{ 
    fprintf(stderr, "Usage: %s dir_name\n", argv[0]); 
    exit(1); 
} 
strncpy(fullpath, argv[1], MAXPATHLEN - 1); /* account for trailing '/' */ 
fullpath[MAXPATHLEN - 1] = '\0'; 
dirnamelen = strlen(fullpath); 
if (strlen(argv[1]) > dirnamelen) { 
    fprintf(stderr, "Directory name is too long: %s", argv[1]); 
    exit(2); 
} 

fullpath[dirnamelen++] = '/'; 
fullpath[dirnamelen] = '\0'; 

if ((dirp = opendir(argv[1])) == NULL) 
{ 
    perror(argv[1]); 
    exit(2); 
} 

while ((direntp = readdir(dirp)) != NULL) 
{ 
    fullpath[dirnamelen] = '\0'; 
    if ((dirnamelen + strlen(direntp->d_name)) > MAXPATHLEN) { 
    fprintf(stderr, "File %s + directory %s is too long.", direntp->d_name, fullpath); 
    continue; 
    } else { 
    /* strncpy is mild overkill because the if statement has verified that 
     there's enough space. */ 
    strncpy(fullpath + dirnamelen, direntp->d_name, MAXPATHLEN - dirnamelen); 
    fullpath[MAXPATHLEN] = '\0'; 
    } 
    if (stat(fullpath, &stat_buf)==-1) 
    { 
    perror("stat ERROR"); 
    exit(3); 
    } 
    if (S_ISREG(stat_buf.st_mode)) str = "regular"; 
    else if (S_ISDIR(stat_buf.st_mode)) str = "directory"; 
    else str = "other"; 
    printf("%-25s - %s\n", direntp->d_name, str); 
} 

closedir(dirp); 
exit(0); 
} 

Notez que j'utilise MAXPATHLEN (de <limits.h>) et vérifiez soigneusement pour vous assurer qu'il n'y a pas de dépassements de tampon. Vous devriez faire la même chose dans votre code.

Édition: Modification du code pour utiliser strn fonctions de la famille pour plus de sécurité.

+0

Pourquoi ne pas utiliser snprintf au lieu de strcpy? –

+0

@Thomas, pour deux raisons. Premièrement, je peux rapporter les erreurs avec plus de précision, d'autre part ma méthode est un peu plus efficace. – Omnifarious

+0

Je comprends votre point de vue, mais je ne pense pas que le gain minime en efficacité est plus important que la lisibilité du code ici. De plus, la manipulation manuelle des chaînes ajoute toujours un risque d'introduction d'un bug. plus de lignes de code -> plus de bugs ... –

2

Ajouter

#include <unistd.h> 
... 
chdir(argv[1]); 

ou appelez stat avec le chemin complet comme celui-ci

... 
char fullpath[MAXPATHLEN]; 
snprintf(fullpath, sizeof(fullpath), "%s/%s", argv[1], direntp->d_name); 
if (stat(fullpath, &stat_buf) == -1) 
... 
+0

Il est possible que les fonctions de la famille 'strn' serait préférable d'utiliser. Moins de vérification d'erreur.OTOH, il est également difficile de signaler correctement une condition d'erreur (certes rare). – Omnifarious

+0

en utilisant chdir semble beaucoup plus simple. Est-ce une mauvaise pratique de l'utiliser? – nunos

+0

btw, je l'instruction snprintf provoque une erreur de segmentation ... – nunos

-2

Si vous utilisez unix, vous pouvez utiliser la commande système.

system("ls -ltr | grep -d"); 
0

D'autres ont suggéré la construction d'un chemin complet pour stat(), ou en utilisant chdir(). Les deux fonctionneront (bien qu'ils soient soumis à une condition de concurrence, si le répertoire est renommé pendant que vous êtes en train de le lire).

Une alternative, qui n'est pas soumise à la condition de concurrence, et est donc sans doute plus "correcte", est d'utiliser fstatat(). Il suffit de remplacer votre appel stat() existant:

fstatat(dirfd(dirp), direntp->d_name, &stat_buf, 0) 

(La méthode chdir() peut être sans condition course aussi: soit en utilisant fchdir(dirfd(dirp)) au lieu de chdir(), ou en changeant de répertoire à argv[1] puis ouvrir "." avec opendir(). La méthode de construction du chemin d'accès ne peut pas être exempte de conditions de course).

1

Pourquoi ne pas essayer cela? Il suffit de donner le chemin de argv[1] comme celui-ci /home/sabri/Desktop/Test

int main(int argc, char *argv[]) 
{ 
    struct dirent *direntp; 
    DIR *dirp; 

    if (argc != 2) 
    { 
     fprintf(stderr, "Usage: %s directory_name\n", argv[0]); 
     return 1; 
    } 

if ((dirp = opendir(argv[1])) == NULL) 
{ 
    perror ("Failed to open directory"); 
    return 1; 
} 

while ((direntp = readdir(dirp)) != NULL) 
    printf("%s\n", direntp->d_name); 

while ((closedir(dirp) == -1) && (errno == EINTR)) ; 

return 0; 

}

Questions connexes