반응형
프로그램을 개발하다보면 특정 디렉토리에 포함된 디렉토리명 또는 파일명을 읽어야 하는 경우가 종종 생깁니다. 예를 들면 특정 디렉토리의 하위의 모든 파일 중에서 .bak 파일을 지운다든 지... 이를 위해서 UNIX/LINUX에서 제공되는 Library call 함수는 아래와 같이 set로 제공합니다. 이들 함수들은 dirent.h에 포함되어 있습니다.
#include <sys/types.h>
#include <dirent.h>
DIR * opendir(const char *name);
struct dirent *readdir(DIR *dir);
int closedir(DIR *dir);
void rewinddir(DIR *dir);
off_t telldir(DIR *dir);
void seekdir(DIR *dir, off_t offset);
위의 함수들의 구조는 fopen(3), fread(3), fclose(3), rewind(3), ftell(3), fseek(3)과 사용방법이 비슷하게 mapping됩니다. 파일은 데이터를 바이트 단위로 읽고 쓰고를 하기 때문에 fread에 대해 바이트 단위 size가 지정되지만 readdir은 struct direct * 단위로 읽기 밖에 안되기 때문에 size 지정없이 return 됩니다.
Example 1). 로그인한 user의 home 디렉토리의 파일 및 디렉토리 목록을 출력하기
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <dirent.h>
int main(int argc, char **argv)
{
DIR *dir_ptr = NULL;
struct dirent *file = NULL;
char home[1024];
strncpy(home, getenv("HOME"), sizeof(home));
/* 목록을 읽을 디렉토리명으로 DIR *를 return 받습니다. */
if((dir_ptr = opendir(home)) == NULL)
{
fprintf(stderr, "%s directory 정보를 읽을 수 없습니다.\n", home);
return -1;
}
/* 디렉토리의 처음부터 파일 또는 디렉토리명을 순서대로 한개씩 읽습니다. */
while((file = readdir(dir_ptr)) != NULL)
{
/*
* struct dirent *의 구조체에서 d_name 이외에는
* 시스템마다 항목이 없을 수 있으므로 무시하고 이름만 사용합니다.
*/
printf("%s\n", file->d_name);
}
/* open된 directory 정보를 close 합니다. */
closedir(dir_ptr);
return 0;
}
위에서 사용하지 않은 함수들의 기능은 다음과 같습니다.
void rewinddir(DIR *dir);
- 디렉토리 읽기 위치를 처음으로 이동시킴니다.
이후에 readdir(3)하면 처음부터 다시 읽게 됩니다.
off_t telldir(DIR *dir);
- 현재 읽을 위치 정보를 return 합니다.
void seekdir(DIR *dir, off_t offset);
- 현재 읽을 위치 정보를 변경합니다.
이 함수를 사용시에 주의할 점은 offset 값은 반드시 telldir(3)로 얻은 값을 저장하고 있다가 설정해야 합니다.
Example 2).
다음 예문은 재귀함수로서 특정 디렉토리 아래의 모든 파일을 뒤져서 파일의 확장자가 .bak이면 삭제
#include <stdio.h>
#include <string.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
int remove_all_backup_file(const char *path)
{
DIR * dir_ptr = NULL;
struct dirent *file = NULL;
struct stat buf;
char *ext;
char filename[1024];
/* 목록을 읽을 디렉토리명으로 DIR *를 return 받습니다. */
if((dir_ptr = opendir(path)) == NULL)
{
return -1;
}
/* 디렉토리의 처음부터 파일 또는 디렉토리명을 순서대로 한개씩 읽습니다. */
while((file = readdir(dir_ptr)) != NULL)
{
// readdir 읽혀진 파일명 중에 현재 디렉토리를 나타네는 . 도 포함되어 있으므로
// 무한 반복에 빠지지 않으려면 파일명이 . 이거나 .. 이면 skip 해야 함
if(strcmp(file->d_name, ".") == 0 || strcmp(file->d_name, "..") == 0)
{
continue;
}
sprintf(filename, "%s/%s", path, file->d_name);
/* 파일의 속성(파일의 유형, 크기, 생성/변경 시간 등을 얻기 위하여 */
if(stat(filename, &buf) == -1)
{
continue;
}
if(S_ISDIR(buf.st_mode)) // 검색된 이름의 속성이 디렉토리이면
{
/*
* 검색된 파일이 directory이면 재귀호출로 하위 디렉토리를 다시 검색
*/
remove_all_backup_file(filename);
}
else if(S_ISREG(buf.st_mode)) // 검색된 이름의 속성이 일반파일이면
{
if((ext = strrchr(filename, '.')) == NULL)
{
continue;
}
if(strcmp(ext, ".bak") == 0) // 파일의 확장자가 .bak 이면 파일을 삭제함
{
unlink(filename);
}
}
}
/* open된 directory 정보를 close 합니다. */
closedir(dir_ptr);
return 0;
}
int main(int argc, char **argv)
{
int idx;
for(idx = 1; idx < argc; idx++) {
printf("%s 디렉토리 하위에 있는 모든 .bak 파일을 지웁니다\n", argv[idx]);
remove_all_backup_file(argv[idx]);
}
}
위의 재귀호출 함수를 일부 수정하면 하위 디렉토리의 파일들을 일괄로 처리하는 프로그램 작성을 쉽게 할 수 있습니다.
see also :
반응형
'C언어 > 고급' 카테고리의 다른 글
va_arg(3), va_start(3), va_end(3) 활용 - 동적 parameter 함수 만들기 (0) | 2019.10.07 |
---|---|
dlopen(3), dlsym(3), dlclose(3) 활용 - dynamic library 동적 loading (0) | 2019.10.01 |
v...printf(3) 활용 : printf(3)같은 동적 파라미터 함수 만들기 (0) | 2019.09.22 |
dup2(2) 활용 - 표준출력/표준오류를 파일로 생성하는 방법 (0) | 2019.09.21 |
popen(3), pclose(3)의 활용 - 다른 프로세스의 표준 입/출력 제어하기 (0) | 2019.09.21 |