반응형

프로그램을 개발하다보면 특정 디렉토리에 포함된 디렉토리명 또는 파일명을 읽어야 하는 경우가 종종 생깁니다. 예를 들면 특정 디렉토리의 하위의 모든 파일 중에서 .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 :

    File 속성 정보 및 파일 관리 Library

    Directory 정보 조회 및 관리 Library

    System Call File I/O Library

    Stream File I/O Library

 

 

 

반응형
블로그 이미지

자연&사람

행복한 개발자 programmer since 1995.

,