본문 바로가기
리눅스/프로그래밍

[리눅스] scandir() 함수를 이용하여 구현하는 ls 명령어(command)

by hseoy 2020. 11. 6.
반응형

여기서는 아주 간단한 형태의 ls 명령을 구현하려고 한다. ls라 함은 리눅스 환경에서 디렉토리(or 폴더)의 내용을 보는 명령을 의미한다. 그리고 이런 ls 명령의 아주 간단한 형태는 단순하게 내부에 있는 파일들/디렉토리들의 이름들을 공백을 기준으로 출력하는 것을 말한다.

먼저 scandir() 함수에 대한 소개를 먼저 하려고 한다.

#include <dirent.h>

int scandir(const char *dirp, struct dirent ***namelist,
        int (*filter)(const struct dirent *),
        int (*compar)(const struct dirent **, const struct dirent **));

scandir() 함수는 인자로 dirp에 디렉토리 이름을 주면 해당 디렉토리에 있는 모든 파일 및 디렉토리 목록을 함수포인터 형태로 넘겨준 filter 함수에서 거르고 compar 함수의 비교 조건을 기준으로 정렬하여 결과를 namelist에 동적할당해서 넣어주는 함수이다. 반환값은 디렉토리 내부에 있는 파일/디렉토리 개수이다. 이 함수를 사용하면 파일명/디렉토리명을 알파벳 순서 대로, 혹은 생성 시간 순서 대로 정렬하거나 filter()로 특정 규칙의 파일/디렉토리만 가져올 수도 있다. scandir() 함수에 대한 자세한 내용은 리눅스에서 "man scandir"이라고 타이핑하면 볼 수 있으므로 이 정도에서 설명을 마치겠다. 

만약 filter를 사용하지 않으려면 filter에 NULL을 넘겨주면 된다. 그리고 compar의 경우 이름을 기준으로 정렬할 것이므로 라이브러리 내 이미 구현된 alphasort를 사용하도록 하겠다. 

위에서 설명한 내용을 기반으로 scandir에 대한 아주 간단한 사용을 보자면 아래와 같다. 이 코드는 name_list에 scandir을 사용하여 현재 디렉토리의 내용들을 가져오는 코드다. name_list는 scandir에 의해 동적할당 되므로 종료 하기 전에 name_list들이 가리키는 모든 메모리와 name_list를 free() 함수를 사용하여 할당 해제 해줘야 한다. 여기서는 SAFE_FREE라는 매크로를 정의하여 혹시 모를 오류를 방지했다.

#include <stdlib.h>
#include <dirent.h>

#define SAFE_FREE(p) {if(p!=NULL){free(p);p=NULL;}}

int main()
{
    int i = 0;
    int name_count = 0;
    struct dirent** name_list = NULL;
    
    name_count = scandir(".", &name_list, NULL, alphasort);
    
    for (i = 0; i < name_count; i++) {
        SAFE_FREE(name_list[i]);
    }
    SAFE_FREE(name_list);
    
    return 0;
}

이제 위 코드를 이해했다고 가정하면 dirent 구조체의 내용을 알고 있기만 하면 ls 명령의 기본적인 형태를 작성할 수 있을 거라 생각된다. dirent 구조체의 내용은 아래와 같으며 ls 명령을 구현하는 데 필요한 정보는 d_name 필드이다.

struct dirent
{
    long d_ino; // 아이노드
    off_t d_off; // dirent의 offset
    unsigned short d_reclen; // d_name의 길이
    char d_name [NAME_MAX+1]; // 파일/디렉토리 이름
}

우리는 디렉토리/파일 이름들을 공백을 기준으로 출력하고자 하는 것이기에 결론적으로 d_name을 공백을 두고 반복해서 출력하면 된다. name_count만큼. 그리고 이것을 위 코드에 덧붙이면 아래와 같다

#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>

#define SAFE_FREE(p) {if(p!=NULL){free(p);p=NULL;}}

int main()
{
    int i = 0;
    int name_count = 0;
    struct dirent** name_list = NULL;
    
    name_count = scandir(".", &name_list, NULL, alphasort);
    
    for (i = 0; i < name_count; i++) {
        printf("%s ", name_list[i]->d_name);
        SAFE_FREE(name_list[i]);
    }
    printf("\n");
    SAFE_FREE(name_list);
    
    return 0;
}

.

반응형

댓글