GNU/Linux >> Belajar Linux >  >> Linux

Bagaimana cara mendaftar direktori secara rekursif di C di Linux?

Ini adalah versi rekursif:

#include <unistd.h>
#include <sys/types.h>
#include <dirent.h>
#include <stdio.h>
#include <string.h>

void listdir(const char *name, int indent)
{
    DIR *dir;
    struct dirent *entry;

    if (!(dir = opendir(name)))
        return;

    while ((entry = readdir(dir)) != NULL) {
        if (entry->d_type == DT_DIR) {
            char path[1024];
            if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
                continue;
            snprintf(path, sizeof(path), "%s/%s", name, entry->d_name);
            printf("%*s[%s]\n", indent, "", entry->d_name);
            listdir(path, indent + 2);
        } else {
            printf("%*s- %s\n", indent, "", entry->d_name);
        }
    }
    closedir(dir);
}

int main(void) {
    listdir(".", 0);
    return 0;
}

Mengapa semua orang bersikeras untuk menemukan kembali rodanya lagi dan lagi?

POSIX.1-2008 membakukan nftw() fungsi, juga didefinisikan dalam Spesifikasi Unix Tunggal v4 (SuSv4), dan tersedia di Linux (glibc, man 3 nftw ), OS X, dan varian BSD terbaru. Ini sama sekali bukan hal baru.

Naif opendir() /readdir() /closedir() implementasi berbasis - hampir tidak pernah menangani kasus di mana direktori atau file dipindahkan, diganti namanya, atau dihapus selama traversal pohon, sedangkan nftw() harus menanganinya dengan anggun.

Sebagai contoh, pertimbangkan program C berikut yang mencantumkan pohon direktori mulai dari direktori kerja saat ini, atau di setiap direktori yang disebutkan pada baris perintah, atau hanya file yang disebutkan pada baris perintah:

/* We want POSIX.1-2008 + XSI, i.e. SuSv4, features */
#define _XOPEN_SOURCE 700

/* Added on 2017-06-25:
   If the C library can support 64-bit file sizes
   and offsets, using the standard names,
   these defines tell the C library to do so. */
#define _LARGEFILE64_SOURCE
#define _FILE_OFFSET_BITS 64 

#include <stdlib.h>
#include <unistd.h>
#include <ftw.h>
#include <time.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>

/* POSIX.1 says each process has at least 20 file descriptors.
 * Three of those belong to the standard streams.
 * Here, we use a conservative estimate of 15 available;
 * assuming we use at most two for other uses in this program,
 * we should never run into any problems.
 * Most trees are shallower than that, so it is efficient.
 * Deeper trees are traversed fine, just a bit slower.
 * (Linux allows typically hundreds to thousands of open files,
 *  so you'll probably never see any issues even if you used
 *  a much higher value, say a couple of hundred, but
 *  15 is a safe, reasonable value.)
*/
#ifndef USE_FDS
#define USE_FDS 15
#endif

int print_entry(const char *filepath, const struct stat *info,
                const int typeflag, struct FTW *pathinfo)
{
    /* const char *const filename = filepath + pathinfo->base; */
    const double bytes = (double)info->st_size; /* Not exact if large! */
    struct tm mtime;

    localtime_r(&(info->st_mtime), &mtime);

    printf("%04d-%02d-%02d %02d:%02d:%02d",
           mtime.tm_year+1900, mtime.tm_mon+1, mtime.tm_mday,
           mtime.tm_hour, mtime.tm_min, mtime.tm_sec);

    if (bytes >= 1099511627776.0)
        printf(" %9.3f TiB", bytes / 1099511627776.0);
    else
    if (bytes >= 1073741824.0)
        printf(" %9.3f GiB", bytes / 1073741824.0);
    else
    if (bytes >= 1048576.0)
        printf(" %9.3f MiB", bytes / 1048576.0);
    else
    if (bytes >= 1024.0)
        printf(" %9.3f KiB", bytes / 1024.0);
    else
        printf(" %9.0f B  ", bytes);

    if (typeflag == FTW_SL) {
        char   *target;
        size_t  maxlen = 1023;
        ssize_t len;

        while (1) {

            target = malloc(maxlen + 1);
            if (target == NULL)
                return ENOMEM;

            len = readlink(filepath, target, maxlen);
            if (len == (ssize_t)-1) {
                const int saved_errno = errno;
                free(target);
                return saved_errno;
            }
            if (len >= (ssize_t)maxlen) {
                free(target);
                maxlen += 1024;
                continue;
            }

            target[len] = '\0';
            break;
        }

        printf(" %s -> %s\n", filepath, target);
        free(target);

    } else
    if (typeflag == FTW_SLN)
        printf(" %s (dangling symlink)\n", filepath);
    else
    if (typeflag == FTW_F)
        printf(" %s\n", filepath);
    else
    if (typeflag == FTW_D || typeflag == FTW_DP)
        printf(" %s/\n", filepath);
    else
    if (typeflag == FTW_DNR)
        printf(" %s/ (unreadable)\n", filepath);
    else
        printf(" %s (unknown)\n", filepath);

    return 0;
}


int print_directory_tree(const char *const dirpath)
{
    int result;

    /* Invalid directory path? */
    if (dirpath == NULL || *dirpath == '\0')
        return errno = EINVAL;

    result = nftw(dirpath, print_entry, USE_FDS, FTW_PHYS);
    if (result >= 0)
        errno = result;

    return errno;
}

int main(int argc, char *argv[])
{
    int arg;

    if (argc < 2) {

        if (print_directory_tree(".")) {
            fprintf(stderr, "%s.\n", strerror(errno));
            return EXIT_FAILURE;
        }

    } else {

        for (arg = 1; arg < argc; arg++) {
            if (print_directory_tree(argv[arg])) {
                fprintf(stderr, "%s.\n", strerror(errno));
                return EXIT_FAILURE;
            }
        }

    }

    return EXIT_SUCCESS;
}

Sebagian besar kode di atas ada di print_entry() . Tugasnya adalah mencetak setiap entri direktori. Di print_directory_tree() , kami memberi tahu nftw() untuk memanggilnya untuk setiap entri direktori yang dilihatnya.

Satu-satunya detail bergelombang tangan di atas adalah keputusan tentang berapa banyak deskriptor file yang harus dibiarkan nftw() menggunakan. Jika program Anda menggunakan paling banyak dua deskriptor file tambahan (selain aliran standar) selama berjalannya hierarki file, 15 diketahui aman (pada semua sistem yang memiliki nftw() dan sebagian besar sesuai dengan POSIX).

Di Linux, Anda bisa menggunakan sysconf(_SC_OPEN_MAX) untuk menemukan jumlah maksimum file yang terbuka, dan kurangi jumlah yang Anda gunakan secara bersamaan dengan nftw() panggilan, tetapi saya tidak akan repot (kecuali saya tahu utilitas akan digunakan sebagian besar dengan struktur direktori yang dalam secara patologis). Lima belas deskriptor tidak batasi kedalaman pohon; nftw() hanya menjadi lebih lambat (dan mungkin tidak mendeteksi perubahan dalam direktori jika menjalankan direktori lebih dalam dari 13 direktori dari direktori itu, meskipun pengorbanan dan kemampuan umum untuk mendeteksi perubahan bervariasi antara implementasi sistem dan pustaka C). Hanya dengan menggunakan konstanta waktu kompilasi seperti itu membuat kode tetap portabel -- seharusnya bekerja tidak hanya di Linux, tetapi juga di Mac OS X dan semua varian BSD saat ini, dan sebagian besar varian Unix yang tidak terlalu lama juga.

Dalam sebuah komentar, Ruslan menyebutkan bahwa mereka harus beralih ke nftw64() karena mereka memiliki entri sistem file yang membutuhkan ukuran/offset 64-bit, dan versi "normal" dari nftw() gagal dengan errno == EOVERFLOW . Solusi yang tepat adalah tidak beralih ke fungsi 64-bit khusus GLIBC, tetapi mendefinisikan _LARGEFILE64_SOURCE dan _FILE_OFFSET_BITS 64 . Ini memberi tahu pustaka C untuk beralih ke ukuran file 64-bit dan offset jika memungkinkan, saat menggunakan fungsi standar (nftw() , fstat() , dan lain-lain) dan ketik nama (off_t dll.).


Linux
  1. Cara Mencari dan Menghapus Direktori Secara Rekursif di Linux

  2. Cara Menemukan File Di Linux Di Semua Direktori Secara Rekursif

  3. Cara Mengganti Nama Direktori di Linux

  1. Cara Menggunakan Perintah SS Linux

  2. Cara Mendaftar Pengguna di Linux

  3. Bagaimana cara mendapatkan daftar direktori dalam zip?

  1. Cara Mendaftar Grup di Linux

  2. Cara Mendaftar Pekerjaan Cron di Linux

  3. Cara Mengganti Nama Direktori di Linux