-
Unix Programming : 명령어 만들기 : 2/3Computer Science/etc 2020. 6. 15. 16:30728x90
2. mycp source dest 또는 source … directory
1) 구현 아이디어 및 설명
- 1번에서 사용한 getopt함수를 포함하여, stat와 폴더 관련 함수들을 주로 사용하였다. stat함수의 구조는 다음과 같다.
- path에 검색할 파일의 경로를 지정하고, 검색한 정보를 buf에 저장한다. 작성하면서 다시 알게된 점은 path 부분에 파일 이름을 받은 인자가 올 수 있는데, 이 파일이 코드 상의 현재 디렉토리에서 파악이 가능한 파일이어야 적용된다는 점이다.
- 때문에 cp시 하위 폴더를 방문 후, 새로운 폴더를 방문하려면, stat함수를 사용하기 전에 상위 폴더로 변경 후 사용해야 적용된다.
- 폴더 관련 함수 중 주로 사용한 함수는 mkdir과 chdir 이다. mkdir과 chdir함수의 구조는 다음과 같다.
- 이 두 함수를 사용하기 위해, 현재 디렉토리의 위치(path)를 문자열로 저장하기 위해, strcat함수를 이용하여 문자열로 고정하였다. 필요할 때 고정한 문자열을 가져옴으로써 복잡함을 줄였다.
- cp를 구현하면서 가장 헷갈렸던 점은 코드 상 현재 디렉토리가 어디인지 파악하는 것이었다. 이를 쉽게 해결하기 위해, strcat함수를 이용해 문자열로 위치를 고정하고, chdir함수를 이용하여 상위 폴더와 하위 폴더를 옮기며 수행하였다.
2) 프로그램 소스 및 설명문
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <string.h> #include <sys/types.h> #include <sys/stat.h> #include <dirent.h> int main(int argc, char* argv[]){ char ans, buf[BUFSIZ], *path, *pathog, *filename; DIR *dir=NULL; struct dirent *entry=NULL; int rfd, wfd, n, filenum=0, overwrite=0; struct stat st, ft, tt; // 주석문 작성 순서 : 옵션이 없는 경우 -> 옵션이 있는 경우 // 옵션이 있는 경우 while((n=getopt(argc, argv, "firv"))!=-1){ // copy a(argv[2]) to b([argv[3])라고 하자 stat(argv[2], &st); // 파일 a에 대한 정보를 st에 저장한다 switch(n){ case 'f': rfd = open( argv[2], O_RDONLY ); if(rfd==-1){ perror("open"); exit(1); } path=getcwd(NULL,BUFSIZ); // 현재의 경로를 path 문자열에 저장한다 if((dir=opendir(path))==NULL) // opendir함수를 통해 path 경로에 해당하는 디렉토리를 dir 포인터로 한다 { perror("opendir"); exit(1); } while((entry=readdir(dir))!=NULL) // dir 포인터에 해당되는 파일을 entry로 하여 순차 접근한다 { if(strcmp(argv[3], entry->d_name)) // argv[3] 과 dir 포인터가 가리키는 파일의 이름이 같다면, { remove(entry->d_name); // 해당 파일의 제거한다 break; } } wfd = open(argv[3], O_WRONLY | O_CREAT | O_TRUNC, st.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO)); if(wfd==-1){ perror("open"); exit(1); } while((n=read(rfd, buf, BUFSIZ))>0){ if(write(wfd, buf, n)!=n) perror("write"); if(n==-1) perror("read"); } close(rfd); close(wfd); break; case 'i': rfd = open( argv[2], O_RDONLY ); if(rfd==-1){ perror("open"); exit(1); } path=getcwd(NULL,BUFSIZ); if((dir=opendir(path))==NULL) // opendir함수를 통해 cwd 경로에 해당하는 디렉토리를 dir 포인터로 한다 { perror("opendir"); exit(1); } while((entry=readdir(dir))!=NULL){ if(strcmp(argv[3], entry->d_name)) // argv[3]과 dir 포인터가 가리키는 파일의 이름과 같다면 { overwrite=1; // overwrite를 1로 한다 (overwrite를 해야한다는 의미) break; } } if(overwrite==1){ printf("mycp: overwrite ‘test2.txt’? "); // overwrite를 할지 사용자에게 묻는다 scanf("%c", &ans); if(ans=='y') // yes일 경우 { wfd = open(argv[3], O_WRONLY | O_CREAT | O_TRUNC, st.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO)); if(wfd==-1){ perror("open"); exit(1); } while((n=read(rfd, buf, BUFSIZ))>0){ if(write(wfd, buf, n)!=n) perror("write"); if(n==-1) perror("read"); } close(rfd); close(wfd); } else break; } else{ wfd = open(argv[3], O_WRONLY | O_CREAT | O_TRUNC, st.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO)); if(wfd==-1){ perror("open"); exit(1); } while((n=read(rfd, buf, BUFSIZ))>0){ if(write(wfd, buf, n)!=n) perror("write"); if(n==-1) perror("read"); } close(rfd); close(wfd); } break; case 'v': stat(argv[3], &st); // 파일 b에 대한 정보를 st에 저장한다 if(S_ISDIR(st.st_mode)) // 현재 파일이 디렉토리 파일인 경우 { stat(argv[2], &st); // 파일 a에 대한 정보를 st에 저장한다 rfd = open( argv[2], O_RDONLY ); // 파일 a를 읽기 전용으로 열고, rfd에 파일기술자를 저장한다 if(rfd==-1){ perror("open"); exit(1); } chdir(argv[3]); // 파일 b 내부를 현재 디렉토리로 한다 wfd = open( argv[2], O_WRONLY | O_CREAT | O_TRUNC, st.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO)); // 아래와 같이, 마스크를 이용하여 접근 권한을 가져온다 if(wfd==-1){ perror("open"); exit(1); } while((n=read(rfd, buf, BUFSIZ))>0){ if(write(wfd, buf, n)!=n) perror("write"); if(n==-1) perror("read"); } close(rfd); close(wfd); printf("'%s' -> '%s'\n", argv[2], getcwd(NULL, BUFSIZ)); // 복사가 성공한다면, cp -v 와 같은 형식의 출력문을 출력한다 } else{ stat(argv[2], &st); // 파일 a에 대한 정보를 st에 저장한다 rfd = open( argv[2], O_RDONLY ); // 파일 a를 읽기 전용으로 열고, rfd에 파일기술자를 저장한다 if(rfd==-1){ perror("open"); exit(1); } wfd = open( argv[3], O_WRONLY | O_CREAT | O_TRUNC, st.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO)); // 아래와 같이, 마스크를 이용하여 접근 권한을 가져온다 if(wfd==-1){ perror("open"); exit(1); } while((n=read(rfd, buf, BUFSIZ))>0){ if(write(wfd, buf, n)!=n) perror("write"); if(n==-1) perror("read"); } close(rfd); close(wfd); printf("'%s' -> '%s'\n", argv[2], argv[3]); // 복사가 성공한다면, cp -v 와 같은 형식의 출력문을 출력한다 } break; case 'r': if(S_ISDIR(st.st_mode)) // 파일 a가 디렉토리 파일인 경우 { pathog=getcwd(NULL, BUFSIZ); // pathog를 상위 폴더 경로로 하여 저장한다 chdir(argv[2]); // 현재 디렉토리를 디렉토리 파일 a 내부로 한다 path=getcwd(NULL, BUFSIZ); //path를 현재 경로로 하여 저장한다 if((dir=opendir(path))==NULL) // 현재 디렉토리 내의 파일을 순차 접근하기 위해, 현재 디렉토리를 가리키는 포인터를 dir로 한다 { perror("opendir"); exit(1); } while((entry=readdir(dir))!=NULL) // readdir함수를 이용하여 포인터가 가리킨 파일을 entry라고 한다 // 이제부터는 현재 파일이라 함은, 포인터가 가리킨 현재 파일 entry를 뜻한다 { stat(entry->d_name, &ft); // 현재 파일에 대한 정보를 ft에 저장한다 if(S_ISREG(ft.st_mode)) // 현재 파일이 일반 파일인 경우 { filename=entry->d_name; // 현재 파일의 이름을 filename으로 한다 stat(filename, &tt); // filename에 대한 정보를 tt에 저장한다 rfd = open(filename, O_RDONLY); // filename 파일을 읽기 전용으로 연다 if(rfd==-1){ perror("open"); exit(1); } filenum++; // 디렉토리 내부의 파일의 갯수를 파악하기 위한 count이다 if(filenum==1) // 디렉토리 내부의 파일의 갯수가 1인 경우에만 새 폴더를 만들고, // filenum이 그 이상이라면 이 과정은 거치지 않는다 { chdir(pathog); //pathog를 현재 디렉토리로 한다 path=getcwd(NULL, BUFSIZ); strcat(path, "/"); strcat(path, argv[3]); // 새 폴더를 만들 경로를 문자열로 만들기 위한 과정이다 mkdir(path, st.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO)); // 새 폴더를 만들고, 이 폴더에 대한 접근 권한은 상위 폴더의 정보가 담긴 st와 동일하게 한다 pathog=getcwd(NULL, BUFSIZ); // pathog에 현재 디렉토리의 문자열을 저장한다 } chdir(pathog); // pathog를 현재 디렉토리로 한다. 이 과정을 거쳐야 컴파일러가 파일 b의 위치를 알 수 있다 chdir(argv[3]); // 디렉토리 파일 b 내부를 현재 디렉토리로 한다 stat(filename, &tt); // filename에 대한 정보를 tt에 저장한다 (생략 가능)) wfd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, tt.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO)); // 옵션 없는 경우와 동일한 방식이다. // 주의할 점은 filename에 대한 정보는 tt에 저장되어 있기 때문에 마스크 플래그와 tt의 퍼미션을 &연산해야 한다 if(wfd==-1){ perror("open"); exit(1); } while((n=read(rfd, buf, BUFSIZ))>0){ if(write(wfd, buf, n)!=n) perror("write"); if(n==-1) perror("read"); } chdir(pathog); // 상위 폴더를 현재 디렉토리로 한다. 이 과정을 거쳐야 컴파일러가 파일 a의 위치를 알 수 있다 chdir(argv[2]); // 디렉토리 파일 a 내부를 현재 위치로 한다. 다른 파일의 복사를 위함이다 } close(rfd); close(wfd); } } break; } } if(n==-1 && optind==1) // 옵션이 없고, 옵션의 인덱스의 변화 역시 없다면 다음을 실행한다 { // copy a(argv[1]) to b([argv[2])라고 하자 stat(argv[2], &st); // 파일 b에 대한 정보를 st에 저장한다 if(S_ISDIR(st.st_mode)) // 파일 b가 디렉토리 파일인 경우 { stat(argv[1], &st); // 파일 a에 대한 정보를 st에 저장한다 rfd = open( argv[1], O_RDONLY ); // 파일 a를 open함수를 통해 연다 // O_RDONLY는 읽기 전용을 의미하는 플래그 값이다 // 파일 기술자를 리턴한다 if(rfd==-1) // open함수의 오류가 있다면 파일 기술자 값이 -1�� 된다 { perror("open"); exit(1); } chdir(argv[2]); // 파일 b의 위치(디렉토리 파일 내부)를 현재 위치로 한다 wfd = open( argv[1], O_WRONLY | O_CREAT | O_TRUNC, st.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO)); // st.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO)는 파일 a의 접근 권한을 그대로 복사하기 위해 마스크(S_IRWXU | S_IRWXG | S_IRWXO) 플래그와 // 현재 파일의 접근 권한을 & 연산한다. 결과로 어떤 접근 권한을 허용/비허용 했는지 알 수 있다 if(wfd==-1){ perror("open"); exit(1); } while((n=read(rfd, buf, BUFSIZ))>0) // read함수를 이용하고, 문자를 읽어 드릴 임시 공간 buf에 BUFSIZ만큼씩 저장한다 { if(write(wfd, buf, n)!=n) // write함수를 이용하고, buf에 저장된 내용을 쓴다 perror("write"); if(n==-1) perror("read"); } close(rfd); close(wfd); } // 파일 b가 일반 파일인 경우 // 일반 파일인 경우, 위와 같은 방식을 사용하되 디렉토리의 변화가 없다는 점에 차이가 있다 else{ stat(argv[1], &st); rfd = open( argv[1], O_RDONLY ); if(rfd==-1){ perror("open"); exit(1); } wfd = open( argv[2], O_WRONLY | O_CREAT | O_TRUNC, st.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO)); if(wfd==-1){ perror("open"); exit(1); } while((n=read(rfd, buf, BUFSIZ))>0){ if(write(wfd, buf, n)!=n) perror("write"); if(n==-1) perror("read"); } close(rfd); close(wfd); } } return 0; }
3) 기본 기능
(1) cp [file1] [file2]
(2) cp [file1] [dri1]
4) 옵션 기능
(1) -r : 하위 파일을 복사한다.
(2) -v : 복사의 진행 작업을 표시한다.
(3) -i : 복사한 대상이 이미 있다면, 사용자에게 확인한다.
(4) -f : 복사한 대상이 이미 있다면, 기존의 것을 삭제하고 복사한다.
728x90'Computer Science > etc' 카테고리의 다른 글
CS : COCO Dataset (0) 2021.04.09 Unix Programming : 명령어 만들기 : 3/3 (0) 2020.06.15 Unix Programming : 명령어 만들기 : 1/3 (0) 2020.06.15 ProgrammingLanguages : Question : 2 (0) 2020.06.15 ProgrammingLanguages : Question : 1 (0) 2020.06.15