GNU/Linux >> Belajar Linux >  >> Linux

Bagaimana saya bisa membuat pohon direktori di C++/Linux?

system("mkdir -p /tmp/a/b/c")

adalah cara terpendek yang dapat saya pikirkan (dalam hal panjang kode, belum tentu waktu eksekusi).

Ini bukan lintas platform tetapi akan berfungsi di Linux.


Mudah dengan Boost.Filesystem:create_directories

#include <boost/filesystem.hpp>
//...
boost::filesystem::create_directories("/tmp/a/b/c");

Pengembalian:true jika direktori baru telah dibuat, jika tidak false .


Ini adalah contoh kode saya (berfungsi untuk Windows dan Linux):

#include <iostream>
#include <string>
#include <sys/stat.h> // stat
#include <errno.h>    // errno, ENOENT, EEXIST
#if defined(_WIN32)
#include <direct.h>   // _mkdir
#endif

bool isDirExist(const std::string& path)
{
#if defined(_WIN32)
    struct _stat info;
    if (_stat(path.c_str(), &info) != 0)
    {
        return false;
    }
    return (info.st_mode & _S_IFDIR) != 0;
#else 
    struct stat info;
    if (stat(path.c_str(), &info) != 0)
    {
        return false;
    }
    return (info.st_mode & S_IFDIR) != 0;
#endif
}

bool makePath(const std::string& path)
{
#if defined(_WIN32)
    int ret = _mkdir(path.c_str());
#else
    mode_t mode = 0755;
    int ret = mkdir(path.c_str(), mode);
#endif
    if (ret == 0)
        return true;

    switch (errno)
    {
    case ENOENT:
        // parent didn't exist, try to create it
        {
            int pos = path.find_last_of('/');
            if (pos == std::string::npos)
#if defined(_WIN32)
                pos = path.find_last_of('\\');
            if (pos == std::string::npos)
#endif
                return false;
            if (!makePath( path.substr(0, pos) ))
                return false;
        }
        // now, try to create again
#if defined(_WIN32)
        return 0 == _mkdir(path.c_str());
#else 
        return 0 == mkdir(path.c_str(), mode);
#endif

    case EEXIST:
        // done!
        return isDirExist(path);

    default:
        return false;
    }
}

int main(int argc, char* ARGV[])
{
    for (int i=1; i<argc; i++)
    {
        std::cout << "creating " << ARGV[i] << " ... " << (makePath(ARGV[i]) ? "OK" : "failed") << std::endl;
    }
    return 0;
}

Penggunaan:

$ makePath 1/2 folderA/folderB/folderC
creating 1/2 ... OK
creating folderA/folderB/folderC ... OK

Dengan C++17 atau lebih baru, ada header standar <filesystem> dengan fungsistd::filesystem::create_directories yang harus digunakan dalam program C++ modern. Namun, fungsi standar C++ tidak memiliki argumen izin eksplisit (mode) khusus POSIX.

Namun, inilah fungsi C yang dapat dikompilasi dengan kompiler C++.

/*
@(#)File:           mkpath.c
@(#)Purpose:        Create all directories in path
@(#)Author:         J Leffler
@(#)Copyright:      (C) JLSS 1990-2020
@(#)Derivation:     mkpath.c 1.16 2020/06/19 15:08:10
*/

/*TABSTOP=4*/

#include "posixver.h"
#include "mkpath.h"
#include "emalloc.h"

#include <errno.h>
#include <string.h>
/* "sysstat.h" == <sys/stat.h> with fixup for (old) Windows - inc mode_t */
#include "sysstat.h"

typedef struct stat Stat;

static int do_mkdir(const char *path, mode_t mode)
{
    Stat            st;
    int             status = 0;

    if (stat(path, &st) != 0)
    {
        /* Directory does not exist. EEXIST for race condition */
        if (mkdir(path, mode) != 0 && errno != EEXIST)
            status = -1;
    }
    else if (!S_ISDIR(st.st_mode))
    {
        errno = ENOTDIR;
        status = -1;
    }

    return(status);
}

/**
** mkpath - ensure all directories in path exist
** Algorithm takes the pessimistic view and works top-down to ensure
** each directory in path exists, rather than optimistically creating
** the last element and working backwards.
*/
int mkpath(const char *path, mode_t mode)
{
    char           *pp;
    char           *sp;
    int             status;
    char           *copypath = STRDUP(path);

    status = 0;
    pp = copypath;
    while (status == 0 && (sp = strchr(pp, '/')) != 0)
    {
        if (sp != pp)
        {
            /* Neither root nor double slash in path */
            *sp = '\0';
            status = do_mkdir(copypath, mode);
            *sp = '/';
        }
        pp = sp + 1;
    }
    if (status == 0)
        status = do_mkdir(path, mode);
    FREE(copypath);
    return (status);
}

#ifdef TEST

#include <stdio.h>
#include <unistd.h>

/*
** Stress test with parallel running of mkpath() function.
** Before the EEXIST test, code would fail.
** With the EEXIST test, code does not fail.
**
** Test shell script
** PREFIX=mkpath.$$
** NAME=./$PREFIX/sa/32/ad/13/23/13/12/13/sd/ds/ww/qq/ss/dd/zz/xx/dd/rr/ff/ff/ss/ss/ss/ss/ss/ss/ss/ss
** : ${MKPATH:=mkpath}
** ./$MKPATH $NAME &
** [...repeat a dozen times or so...]
** ./$MKPATH $NAME &
** wait
** rm -fr ./$PREFIX/
*/

int main(int argc, char **argv)
{
    int             i;

    for (i = 1; i < argc; i++)
    {
        for (int j = 0; j < 20; j++)
        {
            if (fork() == 0)
            {
                int rc = mkpath(argv[i], 0777);
                if (rc != 0)
                    fprintf(stderr, "%d: failed to create (%d: %s): %s\n",
                            (int)getpid(), errno, strerror(errno), argv[i]);
                exit(rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
            }
        }
        int status;
        int fail = 0;
        while (wait(&status) != -1)
        {
            if (WEXITSTATUS(status) != 0)
                fail = 1;
        }
        if (fail == 0)
            printf("created: %s\n", argv[i]);
    }
    return(0);
}

#endif /* TEST */

Makro STRDUP() dan FREE() adalah versi pemeriksaan kesalahan dari strdup() dan free() , dideklarasikan dalam emalloc.h (dan diimplementasikan dalamemalloc.c dan estrdup.c )."sysstat.h" header berurusan dengan versi rusak dari <sys/stat.h> dan dapat diganti dengan <sys/stat.h> pada sistem Unix modern (tetapi ada banyak masalah di tahun 1990). Dan "mkpath.h" mendeklarasikan mkpath() .

Perubahan antara v1.12 (versi asli jawaban) dan v1.13 (versi jawaban yang diubah) adalah tes untuk EEXIST dido_mkdir() .Hal ini ditunjukkan sebagaimana diperlukan oleh Switch — terima kasih, Switch. Kode pengujian telah ditingkatkan dan mereproduksi masalah pada MacBookPro (Intel Core i7 2,3GHz, menjalankan Mac OS X 10.7.4), dan menunjukkan bahwa masalah telah diperbaiki di revisi (tetapi pengujian hanya dapat menunjukkan adanya bug, tidak pernah ada). Kode yang ditampilkan sekarang adalah v1.16; ada perubahan kosmetik atau administratif yang dilakukan sejak v1.13 (seperti penggunaan mkpath.h bukannya jlss.h dan sertakan <unistd.h> tanpa syarat hanya dalam kode pengujian). Masuk akal untuk berpendapat bahwa "sysstat.h" harus diganti dengan <sys/stat.h> kecuali jika Anda memiliki sistem bandel yang tidak biasa.

(Anda dengan ini diberi izin untuk menggunakan kode ini untuk tujuan apa pun dengan atribusi.)

Kode ini tersedia di repositori SOQ (Stack Overflow Questions) saya di GitHub sebagai file mkpath.c dan mkpath.h (dll.) di sub-direktori thesrc/so-0067-5039.


Linux
  1. Cara Menemukan File Terlama Di Pohon Direktori Di Linux

  2. Bagaimana cara membuat direktori sementara di C++?

  3. Bagaimana kita bisa membuat banyak antarmuka tiruan di Linux?

  1. Cara Membuat Direktori Bersama untuk Semua Pengguna di Linux

  2. Cara Menambahkan Direktori ke PATH di Linux [Dengan Contoh]

  3. Cara Membuat Direktori Baru di Linux

  1. Cara Menggunakan Perintah mkdir untuk Membuat atau Membuat Direktori Linux

  2. Bisakah saya menambahkan pintasan untuk mengganti jalur di Linux?

  3. Bagaimana cara membuat perintah khusus di Unix/Linux?