시스템프로그래밍

[시스템프로그래밍] 리눅스 간단한 ls 구현

Below_zero 2025. 3. 6. 16:02
# 간단한 ls
#include <stdio.h>
#include <sys/types.h>
#include <dirent.h>

void do_ls(char []);

int main(int ac, char *av[]) 
{
	if (ac==1) // 인자(목적 디렉토리)를 넣지 않았을 경우
		do_ls(".");
	else
		while(--ac) // 인자를 넣었을 경우
		{
			printf("%s:\n", *++av);
			do_ls(*av);
		}
	return 0;
}

void do_ls(char dirname[])
{
	DIR *dir_ptr; // 디렉토리를 열기 위한 포인터
	struct dirent *direntp; // 파일의 정보를 받아오기 위한 구조체, 아래 dirent 구조체 참고

	if((dir_ptr = opendir(dirname))==NULL) // 오류 처리
		fprintf(stderr, "ls1: cannot open %s\n", dirname);
	else
	{
		while((direntp=readdir(dir_ptr)) != NULL) // 현재 디렉토리의 파일을 하나씩 읽어온다. 더이상 읽을 파일이 없으면 NULL
			printf("%s\n", direntp->d_name);
		closedir(dir_ptr);
	}
}

 

현재 디렉토리(.), 상위 디렉토리(..)을 포함한 디렉토리 내 파일들이 순서없이 출력된다.



위에서 사용되는 dirent 구조체는 아래와 같이 구성되어 있다.

// dirent 구조체 참고
struct dirent{
    long d_ino; // 파일이 가지고 있는 자신만의 번호 inode를 가리키는 숫자
    off_t d_off; // dirent의 offset
    unsigned short d_reclen; // d_name의 길이
    char d_name [NAME_MAX+1]; //파일 혹은 디렉토리의 이름(없다면 NULL로 종료)
}




 

아래는 ls -l 구현이다.

#include <stdio.h>
#include <sys/types.h>
#include <dirent.h>
#include <sys/stat.h>
#include <string.h>

void do_ls(char[]);
void dostat(char *);
void show_file_info(char *, struct stat *);
void mode_to_letters(int, char[]);
char *uid_to_name(uid_t);
char *gid_to_name(gid_t);

int main(int ac, char *av[])
{
	if(ac==1)
		do_ls(".");
	else
		while(--ac)
		{
			printf("%s:\n", *++av);
			do_ls(*av);
		}
	return 0;
}

void do_ls(char dirname[])
{
	DIR *dir_ptr;
	struct dirent *direntp;

	if ((dir_ptr = opendir(dirname)) == NULL)
		fprintf(stderr, "ls1: cannot open %s\n", dirname);
	else
	{
		while((direntp = readdir(dir_ptr)) != NULL)
			dostat(direntp->d_name);
		closedir(dir_ptr);
	}
}

void dostat(char *filename)
{
	struct stat info; // 파일의 상세 정보를 받아오는 stat 구조체

	if (stat(filename, &info) == -1)
		perror(filename);
	else
		show_file_info(filename, &info);
}

void show_file_info(char *filename, struct stat *info_p)
{
	char *uid_to_name(), *ctime(), *gid_to_name(), *filemode();
	void mode_to_letters();
	char modestr[11];

	mode_to_letters( info_p->st_mode, modestr ); // modestr 문자열에, 해당 파일의 사용자 권한 모드를 파악해 입력 ex) drwxrwxrwx

	printf( "%s"    , modestr);
	printf( "%4d "  , (int) info_p->st_nlink); // link 개수
	printf( "%-8s " , uid_to_name(info_p->st_uid) ); // 소유 user id
	printf( "%-8s " , gid_to_name(info_p->st_gid) ); // 소유 group id
	printf( "%8ld " , (long)info_p->st_size); // 파일 size (byte)
	printf( "%.12s ", 4+ctime(&info_p->st_mtime)); // 최종 수정시간
	printf( "%s\n"  , filename); // 파일 이름
}

void mode_to_letters(int mode, char str[])
{
	strcpy( str, "----------");
	if ( S_ISDIR(mode) ) str[0] = 'd';
	if ( S_ISCHR(mode) ) str[0] = 'c';
	if ( S_ISBLK(mode) ) str[0] = 'b';

	if ( mode & S_IRUSR ) str[1] = 'r';
	if ( mode & S_IWUSR ) str[2] = 'w';
	if ( mode & S_IXUSR ) str[3] = 'x';

	if ( mode & S_IRGRP ) str[4] = 'r';
	if ( mode & S_IWGRP ) str[5] = 'w';
	if ( mode & S_IXGRP ) str[6] = 'x';

	if ( mode & S_IROTH ) str[7] = 'r';
	if ( mode & S_IWOTH ) str[8] = 'w';
	if ( mode & S_IXOTH ) str[9] = 'x';
}

#include <pwd.h>

char *uid_to_name( uid_t uid )
{
	struct passwd *getpwuid(), *pw_ptr;
	static char numstr[10];

	if ((pw_ptr = getpwuid(uid)) == NULL)
	{
		sprintf(numstr, "%d", uid);
		return numstr;
	}
	else
		return pw_ptr->pw_name;
}

#include <grp.h>

char *gid_to_name( gid_t gid )
{
	struct group *getgrgid(), *grp_ptr;
	static char numstr[10];

	if ((grp_ptr = getgrgid(gid)) == NULL)
	{
		sprintf(numstr, "%d", gid);
		return numstr;
	}
	else
		return grp_ptr->gr_name;
}

 

 

위 코드에서 사용되는 stat 구조체의 멤버는 아래와 같아서 ls -l 출력에 필요한 정보들을 뽑아 쓰는 방식이다.

//stat 구조체 참고
struct stat {
    dev_t st_dev;    // 장치 정보(major number, minor number 등)
    ino_t st_ino;    // 파일의 inode 정보
    mode_t st_mode;    // 파일 허가권 모드
    nlink_t st_nlink;    // 파일 링크의 수
    uid_t st_uid;    // 파일 소유자의 사용자 식별번호(UID)
    gid_t st_gid;    // 파일 소유자가 속한 그룹 식별번호(GID)
    dev_t st_rdev;    // raw 장치의 정보(major num, minor num 등)
    off_t st_size;    // 파일 크기(Bytes 단위)
    time_t st_atime;    // 마지막으로 파일을 읽거나 실행한 시간 정보
    time_t st_mtime;    // 마지막으로 파일을 쓰기(변경)한 시간 정보
    time_t st_xtime;    // 마지막으로 inode 내용을 변경한 시간 정보
    long st_blksize;    // I/O 파일 시스템 블록의 크기 정보
    long st_blocks;    // 파일에 할당된 블록 수
};

 

 

역시 현재 디렉토리(.), 상위 디렉토리(..)는 따로 숨김처리를 하지 않았기에 여과없이, 순서없이 ls -l 옵션대로 출력된다.