C ++ / Linux에서 여러 디렉터리를 쉽게 만드는 방법을 원합니다.
예를 들어 lola.file 파일을 디렉토리에 저장하고 싶습니다.
/tmp/a/b/c
그러나 디렉토리가 없으면 자동으로 생성되기를 원합니다. 작동하는 예가 완벽 할 것입니다.
C ++ / Linux에서 여러 디렉터리를 쉽게 만드는 방법을 원합니다.
예를 들어 lola.file 파일을 디렉토리에 저장하고 싶습니다.
/tmp/a/b/c
그러나 디렉토리가 없으면 자동으로 생성되기를 원합니다. 작동하는 예가 완벽 할 것입니다.
답변:
C ++ 17 이상
에서는 최신 C ++ 프로그램에서 사용해야 <filesystem>
하는 함수가 std::filesystem::create_directories
있는 표준 헤더 가
있습니다. 그러나 C ++ 표준 함수에는 POSIX 특정 명시 적 권한 (모드) 인수가 없습니다.
그러나 여기에 C ++ 컴파일러로 컴파일 할 수있는 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 */
매크로 STRDUP()
및 FREE()
오류 검사의 버전입니다
strdup()
및 free()
, 선언 emalloc.h
(및 구현에
emalloc.c
와 estrdup.c
). "sysstat.h"
의 깨진 버전의 헤더 거래 <sys/stat.h>
및 교체 할 수있는 <sys/stat.h>
현대적인 유닉스 시스템 (그러나 많은 문제가 1990 년에 다시 있었다). 그리고 "mkpath.h"
선언합니다 mkpath()
.
를 v1.12 (응답의 원본)와 v1.13 (응답의 수정 된 버전) 사이의 변화에 대해 테스트 하였다 EEXIST
에서
do_mkdir()
. Switch가 필요하다고 지적했습니다
. 감사합니다. Switch. 테스트 코드가 업그레이드되어 MacBook Pro (2.3GHz Intel Core i7, Mac OS X 10.7.4 실행)에서 문제가 재현되었으며 수정 버전에서 문제가 수정되었음을 제안합니다 (그러나 테스트는 버그의 존재만을 보여줄 수 있음 , 절대 부재). 표시된 코드는 이제 v1.16입니다. v1.13 이후로 외관상 또는 관리적 변경 사항이 있습니다 (예 : 테스트 코드에만 무조건 포함 및 mkpath.h
대신 사용 ). 비정상적으로 반항적 인 시스템이없는 한 대체해야 한다고 주장하는 것이
합리적 입니다.jlss.h
<unistd.h>
"sysstat.h"
<sys/stat.h>
(귀하는 어떤 목적 으로든이 코드를 사용할 수있는 권한을 부여 받았습니다.)
이 코드는
GitHub의 SOQ (Stack Overflow Questions) 저장소에서 파일로 mkpath.c
,
mkpath.h
(등)
src / so-0067-5039
하위 디렉터리에 있습니다.
if (errno != EEXIST) { status = -1; }
mkdir이 실패 할 때 추가하는 것 입니다.
stat()
전에 사용하는 문제입니다 mkdir()
. TOCTOU (체크 시간, 사용 시간) 문제입니다. 동일한 29 개 요소 경로를 생성하는 백그라운드에서 13 개의 프로세스를 실행하는 셸 스크립트로 버그를 간지럽 히려고 시도했지만 문제를 해결하지 못했습니다. 그런 다음 테스트 프로그램을 해킹하여 20 번 포크하고 각 어린이에게 시도해 보았습니다. 그 결과 버그가 발생했습니다. 고정 코드에는 if (mkdir(path, mode) != 0 && errno != EEXIST) status = -1;
. 그것은 버그를 보여주지 않습니다.
jlss.h
, emalloc.h
)입니다. 그러나 코드 내에서 사용할 수 SOQ 파일로 GitHub의에 (스택 오버플로 질문) 저장소 jlss.h
, emalloc.c
그리고 emalloc.h
의 SRC / libsoq의 하위 디렉토리. 당신은해야합니다 posixver.h
도, 그리고 몇 가지 다른 ( debug.h
, stderr.c
, stderr.h
- 나는 당신이 모든 디렉토리에 있어야합니다 필요 그게 생각하지만).
Boost.Filesystem으로 쉽게 : create_directories
#include <boost/filesystem.hpp>
//...
boost::filesystem::create_directories("/tmp/a/b/c");
반환 값 : true
새 디렉토리가 생성 된 경우, 그렇지 않은 경우 false
.
The <filesystem> header is not part of C++11; it is a proposal for C++ TR2 based on the Boost.Filesystem library. Visual C++ 2012 includes an implementation of the proposed library.
system("mkdir -p /tmp/a/b/c")
내가 생각할 수있는 가장 짧은 방법입니다 (반드시 실행 시간이 아니라 코드 길이 측면에서).
크로스 플랫폼은 아니지만 Linux에서 작동합니다.
#include <sys/types.h>
#include <sys/stat.h>
int status;
...
status = mkdir("/tmp/a/b/c", S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
에서 여기 . / tmp, / tmp / a, / tmp / a / b / 및 / tmp / a / b / c에 대해 별도의 mkdirs를 수행해야 할 수도 있습니다. C api에는 -p 플래그와 동일한 것이 없기 때문입니다. 상위 레벨 작업을 수행하는 동안 EEXISTS errno를 반드시 무시하십시오.
("/tmp/",...)
, ("/tmp/a/",...)
, ("/tmp/a/b/",...)
,("/tmp/a/b/c/",...)
다음은 코드 예제입니다 (Windows와 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;
}
용법:
$ makePath 1/2 folderA/folderB/folderC
creating 1/2 ... OK
creating folderA/folderB/folderC ... OK
stat
(관련 __STDC__
).
C ++ 17 파일 시스템 인터페이스에서 시작하는 것은 표준 라이브러리의 일부입니다. 이것은 디렉토리를 생성하기 위해 다음을 가질 수 있음을 의미합니다.
#include <filesystem>
std::filesystem::create_directories("/a/b/c/d")
자세한 정보 : https://en.cppreference.com/w/cpp/filesystem/create_directory
또한 gcc를 사용하면 CFLAGS에 "-std = c ++ 17"이 필요합니다. 그리고 "-lstdc ++ fs"는 LDLIBS로. 후자는 잠재적으로 미래에 필요하지 않을 것입니다.
이것은 이전과 유사하지만 재귀 적으로 역방향이 아닌 문자열을 통해 앞으로 작동합니다. 마지막 실패에 대한 올바른 값으로 errno를 남깁니다. 선행 슬래시가있는 경우 루프 외부에서 하나의 find_first_of ()를 통해 또는 선행 /를 감지하고 pre를 1로 설정하여 피할 수 있었던 루프를 통해 추가 시간이 있습니다. 효율성은 a 첫 번째 루프 또는 사전 루프 호출이며 사전 루프 호출을 사용할 때 복잡성이 (약간) 높아집니다.
#include <iostream>
#include <string>
#include <sys/stat.h>
int
mkpath(std::string s,mode_t mode)
{
size_t pos=0;
std::string dir;
int mdret;
if(s[s.size()-1]!='/'){
// force trailing / so we can handle everything in loop
s+='/';
}
while((pos=s.find_first_of('/',pos))!=std::string::npos){
dir=s.substr(0,pos++);
if(dir.size()==0) continue; // if leading / first time is 0 length
if((mdret=mkdir(dir.c_str(),mode)) && errno!=EEXIST){
return mdret;
}
}
return mdret;
}
int main()
{
int mkdirretval;
mkdirretval=mkpath("./foo/bar",0755);
std::cout << mkdirretval << '\n';
}
"C ++"라고 하셨는데 여기 계신 분들은 모두 "Bash shell"을 생각하시는 것 같습니다.
gnu에 대한 소스 코드를 확인하십시오 mkdir
. 그러면 C ++에서 쉘 명령을 구현하는 방법을 볼 수 있습니다.
bool mkpath( std::string path )
{
bool bSuccess = false;
int nRC = ::mkdir( path.c_str(), 0775 );
if( nRC == -1 )
{
switch( errno )
{
case ENOENT:
//parent didn't exist, try to create it
if( mkpath( path.substr(0, path.find_last_of('/')) ) )
//Now, try to create again.
bSuccess = 0 == ::mkdir( path.c_str(), 0775 );
else
bSuccess = false;
break;
case EEXIST:
//Done!
bSuccess = true;
break;
default:
bSuccess = false;
break;
}
}
else
bSuccess = true;
return bSuccess;
}
그래서 mkdirp()
오늘이 필요 하고이 페이지의 솔루션이 지나치게 복잡하다는 것을 알았습니다. 따라서 나는이 쓰레드를 우연히 발견 한 다른 사람들을 위해 쉽게 복사 할 수있는 상당히 짧은 스 니펫을 썼다. 우리가 왜 그렇게 많은 코드 라인이 필요한지 궁금해했다.
mkdirp.h
#ifndef MKDIRP_H
#define MKDIRP_H
#include <sys/stat.h>
#define DEFAULT_MODE S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH
/** Utility function to create directory tree */
bool mkdirp(const char* path, mode_t mode = DEFAULT_MODE);
#endif // MKDIRP_H
mkdirp.cpp
#include <errno.h>
bool mkdirp(const char* path, mode_t mode) {
// const cast for hack
char* p = const_cast<char*>(path);
// Do mkdir for each slash until end of string or error
while (*p != '\0') {
// Skip first character
p++;
// Find first slash or end
while(*p != '\0' && *p != '/') p++;
// Remember value from p
char v = *p;
// Write end of string at p
*p = '\0';
// Create folder from path to '\0' inserted at p
if(mkdir(path, mode) == -1 && errno != EEXIST) {
*p = v;
return false;
}
// Restore path to it's former glory
*p = v;
}
return true;
}
const 캐스팅을 좋아하지 않고 일시적으로 문자열을 수정하는 것이 마음에 들지 않으면 a strdup()
를 수행 free()
하고 나중에 수행하십시오.
이 게시물은 "Create Directory Tree"에 대해 Google에서 높은 순위를 차지하고 있으므로 Windows에서 작동하는 답변을 게시 할 것입니다. 이것은 UNICODE 또는 MBCS 용으로 컴파일 된 Win32 API를 사용하여 작동합니다. 이것은 위의 Mark의 코드에서 이식되었습니다.
이것이 우리가 작업하는 Windows이기 때문에 디렉토리 구분 기호는 슬래시가 아니라 백 슬래시입니다. 슬래시를 사용하려면 다음 '\\'
으로 변경하십시오 .'/'
다음과 함께 작동합니다.
c:\foo\bar\hello\world
과
c:\foo\bar\hellp\world\
(예 : 후행 슬래시가 필요하지 않으므로 확인할 필요가 없습니다.)
" Windows에서 SHCreateDirectoryEx () 를 사용하십시오"라고 말하기 전에 SHCreateDirectoryEx () 가 더 이상 사용되지 않으며 향후 Windows 버전에서 언제든지 제거 될 수 있습니다.
bool CreateDirectoryTree(LPCTSTR szPathTree, LPSECURITY_ATTRIBUTES lpSecurityAttributes = NULL){
bool bSuccess = false;
const BOOL bCD = CreateDirectory(szPathTree, lpSecurityAttributes);
DWORD dwLastError = 0;
if(!bCD){
dwLastError = GetLastError();
}else{
return true;
}
switch(dwLastError){
case ERROR_ALREADY_EXISTS:
bSuccess = true;
break;
case ERROR_PATH_NOT_FOUND:
{
TCHAR szPrev[MAX_PATH] = {0};
LPCTSTR szLast = _tcsrchr(szPathTree,'\\');
_tcsnccpy(szPrev,szPathTree,(int)(szLast-szPathTree));
if(CreateDirectoryTree(szPrev,lpSecurityAttributes)){
bSuccess = CreateDirectory(szPathTree,lpSecurityAttributes)!=0;
if(!bSuccess){
bSuccess = (GetLastError()==ERROR_ALREADY_EXISTS);
}
}else{
bSuccess = false;
}
}
break;
default:
bSuccess = false;
break;
}
return bSuccess;
}
c:\this\is\a/mixed/path\of\slashes
일반적으로 Windows 슬래시는 백 슬래시입니다. 호출자는이 메서드를 호출하기 전에 경로를 삭제하고 모든 슬래시가 적절한 지 확인해야합니다.
나는 그것이 오래된 질문이라는 것을 알고 있지만 그것은 Google 검색 결과에 높게 나타나고 여기에 제공된 답변은 실제로 C ++가 아니거나 너무 복잡합니다.
내 예제에서 createDirTree ()는 모든 무거운 작업 (오류 검사, 경로 유효성 검사)이 어쨌든 createDir ()에 의해 수행되어야하기 때문에 매우 간단합니다. 또한 createDir ()는 디렉토리가 이미 존재하거나 모든 것이 작동하지 않는 경우 true를 반환해야합니다.
C ++에서 수행하는 방법은 다음과 같습니다.
#include <iostream>
#include <string>
bool createDir(const std::string dir)
{
std::cout << "Make sure dir is a valid path, it does not exist and create it: "
<< dir << std::endl;
return true;
}
bool createDirTree(const std::string full_path)
{
size_t pos = 0;
bool ret_val = true;
while(ret_val == true && pos != std::string::npos)
{
pos = full_path.find('/', pos + 1);
ret_val = createDir(full_path.substr(0, pos));
}
return ret_val;
}
int main()
{
createDirTree("/tmp/a/b/c");
return 0;
}
물론 createDir () 함수는 시스템에 따라 다르며 다른 답변에는 이미 리눅스 용으로 작성하는 방법에 대한 충분한 예제가 있으므로 건너 뛰기로 결정했습니다.
여기에는 많은 접근 방식이 설명되어 있지만 대부분은 코드 경로를 하드 코딩해야합니다. Qt 프레임 워크의 두 가지 클래스 인 QDir 및 QFileInfo를 사용하여이 문제에 대한 쉬운 해결책이 있습니다. 이미 Linux 환경에 있으므로 Qt를 사용하기 쉽습니다.
QString qStringFileName("path/to/the/file/that/dont/exist.txt");
QDir dir = QFileInfo(qStringFileName).dir();
if(!dir.exists()) {
dir.mkpath(dir.path());
}
해당 경로에 대한 쓰기 액세스 권한이 있는지 확인하십시오.
다음 dirname()
은 디렉토리 트리를 상향식으로 탐색 하는 데 사용하는 C / C ++ 재귀 함수 입니다. 기존 조상을 찾는 즉시 중지됩니다.
#include <libgen.h>
#include <string.h>
int create_dir_tree_recursive(const char *path, const mode_t mode)
{
if (strcmp(path, "/") == 0) // No need of checking if we are at root.
return 0;
// Check whether this dir exists or not.
struct stat st;
if (stat(path, &st) != 0 || !S_ISDIR(st.st_mode))
{
// Check and create parent dir tree first.
char *path2 = strdup(path);
char *parent_dir_path = dirname(path2);
if (create_dir_tree_recursive(parent_dir_path, mode) == -1)
return -1;
// Create this dir.
if (mkdir(path, mode) == -1)
return -1;
}
return 0;
}
다른 사람들은 당신에게 정답을 얻었지만 나는 당신이 할 수있는 또 다른 깔끔한 것을 보여줄 것이라고 생각했습니다.
mkdir -p /tmp/a/{b,c}/d
다음 경로를 생성합니다.
/tmp/a/b/d
/tmp/a/c/d
중괄호를 사용하면 동일한 계층 수준에서 한 번에 여러 디렉터리를 만들 수 있지만 -p
옵션은 "필요에 따라 부모 디렉터리 만들기"를 의미합니다.