\
나 ;
처럼 정의되지 않은 특수문자는 처리하지 않음PATH
변수나 상대, 절대 경로에 따라 올바른 명령어를 검색하고 실행해야 함'
안의 내용은 해석하지 않음 (문자 그대로 받아들임)"
안의 내용은 $
를 제외하고 해석하지 않음 (환경변수만 해석하고 나머지는 문자 그대로)<
는 인풋>
는 아웃풋"<<"
는 delimiter가 나올 때까지 현재 소스를 인풋으로 사용 (히스토리 업데이트 필요 없음)">>"
는 append 모드로 아웃풋|
): 각 명령어의 아웃풋이 파이프라인을 통해 다음 명령어의 인풋으로 연결되어야 함$어쩌고
)는 값으로 대체되어야 함$?
는 가장 최근에 실행된 파이프라인의 exit code로 대체되어야 함ctrl-C
ctrl-D
ctrl-\
는 배시에서처럼 작동해야 함ctrl-C
: 새로운 라인에서 새 prompt 출력ctrl-D
: 쉘 종료ctrl-\
: 아무것도 하지 않음&&
, ||
+ 괄호)*
)가 현재 작업 중인 디렉토리에서 작동해야 함echo
+ -n
cd
+ 절대, 상대 경로pwd
export
unset
env
exit
printf
malloc
free
write
open
read
close
fork
wait
waitpid
unlink
execve
dup
dup2
pipe
strerror
exit
👉 pipex 참고
전체적으로 잘 모르겠다... 일단 써봐야 할 듯
#include <stdio.h>
#include <readline/readline.h>
#include <readline/history.h>
brew install readline
brew info readline
으로 경로 확인해서 라이브러리와 헤더 인클루드readline 라이브러리를 사용하려면 컴파일할 때
-lreadline
옵션을 줘야 함 (참고)
readline()
readline(3) - Linux manual page
minishell과 readline
터미널에 프롬프트를 띄우고 라인을 읽어들여서 리턴
char *readline(const char *prompt);
$>
)NULL
이면 아무것도 하지 않음?\n
)가 제거된 상태이며, 따로 free()
해주어야 함EOF
를 만났고 라인이 비어 있으면 NULL
리턴rl_on_new_line()
Tell the update functions that we have moved onto a new (empty) line, usually after outputting a newline.
업데이트 함수들에게 새 라인을 입력받겠다고 알려주는 함수?
int rl_on_new_line(void);
rl_redisplay()
를 실행할 때 필요하다고 함
rl_replace_line()
Replace the contents of rl_line_buffer with text. The point and mark are preserved, if possible. If clear_undo is non-zero, the undo list associated with the current line is cleared.
현재까지 입력된 프롬프트의 문자열을 대체하는 함수
void rl_replace_line(const char *text, int clear_undo);
rl_line_buffer
에 있던 문자열이 인자로 전달된 text
로 대체됨clear_undo
가 0
이 아니면 현재 라인과 연관된 언두 리스트가 초기화..?
ctrl-C
처럼 입력된 내용이 화면에 그대로 출력되지는 않지만 새로운 프롬프트를 띄워줘야할 때rl_replace_line("", 1);
처럼 사용하면 새 프롬프트를 띄울 자리를 깨끗하게 비워주게 된다고 함
rl_redisplay()
Change what's displayed on the screen to reflect the current contents of rl_line_buffer.
화면에 출력된 내용을 현재 rl_line_buffer
에 들어 있는 내용으로 대체
void rl_redisplay(void);
rl_replace_line()
이후에 사용해서 새 프롬프트(readline()
의 인자로 전달한 문자열)를 띄울 때 사용한다고 함
add_history()
Place string at the end of the history list. The associated data field (if any) is set to NULL.
히스토리 추가
void add_history (char *string)
string
을 히스토리 리스트에 추가HIST_ENTRY **the_history_list;
로 선언되며, HIST_ENTRY
구조체는 아래와 같음typedef struct _hist_entry {
char *line;
char *data;
} HIST_ENTRY;
typedef struct _hist_state {
HIST_ENTRY **entries; /* Pointer to the entries themselves. */
int offset; /* The location pointer within this array. */
int length; /* Number of elements within this array. */
int size; /* Number of slots allocated to this array. */
int flags;
} HISTORY_STATE;
프롬프트가 열린 상태에서 키보드 위, 아래 방향키를 이용해서 히스토리의 문자열들을 불러올 수 있음
wait3()
자식 프로세스의 종료를 기다리며, 종료된 프로세스의 상태와 자원 사용량을 알려줌
pid_t wait3(int *stat_loc, int options, struct rusage *rusage);
stat_loc
: 자식 프로세스의 종료 상태를 나타내는 정보options
: 프로세스의 종료 상태 체크 시 전달할 옵션 (필요없으면 0
)rusage
: 자식 프로세스의 리소스 사용량에 대한 정보 저장 (필요없으면 0
)0보다 큰 값
: 상태가 변경된 자식 프로세스의 id0
: options
가 WNOHANG
이고 상태 변경이 없는 경우-1
: 오류
stactloc
매크로
매크로 설명 WIFEXITED(status)
자식 프로세스가 exit()
이나main()
의 리턴으로 정상 종료된 경우true
👉 true
인 경우WEXITESTATUS(status)
exit()
의 인자에서 하위 8비트 값을 리턴WIFSIGNALED(status)
자식 프로세스가 시그널을 받아 비정상적으로 종료된 경우 true
👉 true
인 경우WIFTERMSIG(status)
시그널 번호를 리턴 WIFCOREDUMP(status)
코어 파일이 생성된 경우 true
WIFSTOPPED(status)
종료가 아니라 정지 시그널이 발생한 경우 true
👉 true
인 경우WSTOPSIG(status)
실행을 중단시킨 시그널 번호를 리턴 WIFCONTINUED(status)
작업 제어 중지 이후 실행이 재개되었으면 true
options
매크로
매크로 설명 WCONTINUED
중지되었다가 실행을 재개한 이후 상태가 아직 보고되지 않은 자식도 리턴 WNOHANG
자식 프로세스가 종료되어 있지 않으면 바로 리턴
(이 옵션을 주지 않으면 자식 프로세스가 종료될 때까지 블록 상태가 됨)WUNTRACED
중지되었으나 그 상태가 아직 보고되지 않은 자식도 리턴
(종료 뿐만 아니라 중지 상태도 찾기)
wait4()
특정 자식 프로세스의 종료를 기다리며, 종료된 프로세스의 상태와 자원 사용량을 알려줌
pid_t wait4(pid_t pid, int *stat_loc, int options, struct rusage *rusage);
pid
: 대기할 자식 프로세스 지정-1보다 작은 값
: 해당 값의 절대값과 그룹 id가 일치하는 아무 자식 프로세스-1
: 아무 자식 프로세스0
: 부모 프로세스와 같은 그룹에 있는 아무 자식 프로세스0보다 큰 값
: 해당 값의 pid를 가진 자식 프로세스signal()
시그널 제어
void (*signal(int sig, void (*func)(int));)(int);
sig
: 시그널 번호(*func)(int)
: 시그널을 처리할 핸들러SIG_DFL
을 전달SIG_IGN
전달void *()(int)
, 실패 시 SIG_ERR
리턴SIGKILL
과 SIGSTOP
은 제어할 수 없음execve()
로 실행된 프로세스에서는 시그널 설정이 리셋되지만, 무시된 시그널은 계속 무시됨SIGCHLD
에 대해 SIG_IGN
을 지정하면 자식 프로세스가 종료될 때 좀비 프로세스가 생성되지 않음wait()
하고 있었다면, 모든 자식 프로세스가 종료될 때까지 실행이 연기되었다가 -1
을 리턴MacOS에 정의된 시그널
시그널
시그널은 프로세스가 멈추거나 다시 재개되거나 할 때 발생할 수 있음
시그널에는 두 종류가 존재
1. 프로세스 종료를 야기하는 것
2. 그렇지 않은 것
1번에는 회복 불가능한 오류가 발생하거나 인터럽트 문자(ctrl - C
등)를 입력하는 경우가 존재
kill()
프로세스에 시그널 전송
int kill(pid_t pid, int sig);
sig
를 pid
에 전송sig
에 0
을 보내면 아무 시그널도 전송되지 않지만, 이 함수의 에러 체크 동작을 테스트할 수 있음 (pid
의 유효성 검사)pid
는 wait()
에서처럼 지정 가능0
, 실패 시 -1
리턴
struct stat
struct stat { dev_t st_dev; /* device inode resides on */ ino_t st_ino; /* inode's number */ mode_t st_mode; /* 파일 종류 및 접근 권한 */ nlink_t st_nlink; /* number of hard links to the file */ uid_t st_uid; /* user-id of owner */ gid_t st_gid; /* group-id of owner */ dev_t st_rdev; /* device type, for special file inode */ struct timespec st_atimespec; /* time of last access */ struct timespec st_mtimespec; /* time of last data modification */ struct timespec st_ctimespec; /* time of last file status change */ off_t st_size; /* file size, in bytes */ quad_t st_blocks; /* blocks allocated for file */ u_long st_blksize;/* optimal file sys I/O ops blocksize */ u_long st_flags; /* user defined flags for file */ u_long st_gen; /* file generation number */ };
st_mode
는 아래와 같이 정의되어 있어서 비트&
연산으로 여부 확인 가능// 파일 유형 전체의 비트 or 연산 값 #define S_IFMT 0170000 /* type of file */ // 파일 유형 #define S_IFIFO 0010000 /* named pipe (fifo) */ #define S_IFCHR 0020000 /* character special */ #define S_IFDIR 0040000 /* directory */ #define S_IFBLK 0060000 /* block special */ #define S_IFREG 0100000 /* regular */ #define S_IFLNK 0120000 /* symbolic link */ #define S_IFSOCK 0140000 /* socket */ #define S_IFWHT 0160000 /* whiteout */ // 특수 권한 설정 비트 #define S_ISUID 0004000 /* set user id on execution */ #define S_ISGID 0002000 /* set group id on execution */ #define S_ISVTX 0001000 /* save swapped text even after use */ // 접근 권한 값 #define S_IRUSR 0000400 /* read permission, owner */ #define S_IWUSR 0000200 /* write permission, owner */ #define S_IXUSR 0000100 /* execute/search permission, owner */
stat()
파일 상태 얻기 (파일 크기, 권한, 생성일시, 최종 변경일 등)
int stat(const char *restrict path, struct stat *restrict buf);
path
: 상태를 얻을 파일의 경로buf
: 상태 정보를 저장할 구조체 포인터0
, 실패 시 -1
리턴lstat()
파일 상태 얻기
int lstat(const char *restrict path, struct stat *restrict buf);
stat()
과 다르게 심볼릭 링크는 그 링크 자체의 정보를 얻어옴fstat()
파일 상태 얻기
int fstat(int fildes, struct stat *buf);
fildes
: 파일 디스크립터opendir()
디렉토리 열기
DIR *opendir(const char *filename);
filename
: 열 디렉토리 이름NULL
리턴readdir()
디렉토리 내의 파일과 디렉토리 정보 얻기
struct dirent *readdir(DIR *dirp);
dirp
: opendir()
에서 리턴받은 포인터NULL
리턴struct dirent
는 아래와 같이 정의되어 있음struct dirent
{
long d_ino;
unsigned short d_reclen;
unsigned short d_namlen; /* Length of name in d_name. */
char d_name[260]; /* [FILENAME_MAX] */ /* File name. */
};
closedir()
디렉토리 닫기
int closedir(DIR *dirp);
dirp
: opendir()
에서 리턴받은 포인터0
, 실패 시 -1
리턴errno
마지막으로 발생한 오류 번호
extern int errno
getcwd()
현재 작업 중인 디렉토리의 절대 경로 얻기
char *getcwd(char *buf, size_t size);
buf
: 현재 작업 디렉토리를 저장할 공간이 할당된 포인터NULL
인 경우 필요한 공간이 할당되어 리턴되며, size
는 무시됨free()
해야 함size
: buf
에 할당된 메모리의 크기 (바이트 단위)NULL
리턴chdir()
작업 디렉토리 변경
int chdir(const char *path);
path
: 변경할 디렉토리의 상대 또는 절대 경로0
, 실패 시 -1
리턴tty
teletypewriter
컴퓨터와 상호작용을 위한 디바이스로, OS에서 제공하는 가상 char *
ttyname(int fd);콘솔
isatty()
파일 디스크립터가 터미널인지 확인
int isatty(int fd);
fd
: 파일 디스크립터1
, 아니면 0
리턴ttyname()
터미널 이름 얻기
char *ttyname(int fd);
fd
: 파일 디스크립터NULL
리턴ttyslot()
현재 사용자 터미널의 슬롯 찾기
int ttyslot(void);
0
또는 -1
(시스템 차이가 있다고 함)tty 관련 명령어 사용 예시
int tty_fd = ttyslot(); if (isatty(tty_fd)) printf("valid fd\n"); char *tty_name = ttyname(fd); printf("tty_name: %s\n", tty_name);
ioctl()
ioctl() 디바이스 제어
[Kernel] Linux kernel (3) - ioctl function and make test program
in-out control
디바이스(터미널 등) 컨트롤
int ioctl(int fildes, unsigned long request, ...);
fileds
: 열려 있는 파일 디스크립터request
: 디바이스에 전달할 명령 (sys/ioctl.h에 정의된 매크로를 수행할 수도 있음)...
: request
수행 시 필요한 인자가 있다면 뒤에 원하는대로 전달-1
리턴read()
나 write()
로만 해결되지 않는 제어를 하거나 디바이스의 상태를 얻기 위해 사용getenv()
환경변수 값 구하기
char *getenv(const char *name);
name
: 환경변수명NULL
리턴termios 구조체
https://blog.naver.com/choi125496/130034222760
struct termios
TERMinal In Out interfaces Structures
struct termios { tcflag_t c_iflag; /* input flags */ tcflag_t c_oflag; /* output flags */ tcflag_t c_cflag; /* control flags */ tcflag_t c_lflag; /* local flags */ cc_t c_cc[NCCS]; /* control chars */ speed_t c_ispeed; /* input speed */ speed_t c_ospeed; /* output speed */ };
tcsetattr()
터미널 속성 얻기
int tcgetattr(int fildes, struct termios *termios_p);
fildes
: 열려 있는 파일 디스크립터termios_p
: 터미널의 정보를 저장할 구조체 포인터0
, 실패 시 -1
리턴tcgetattr()
터미널 속성 설정
int tcsetattr(int fildes, int optional_actions, const struct termios *termios_p);
fildes
: 열려 있는 파일 디스크립터optional_actions
: 아래의 매크로들로 설정할 수 있음TCSANOW
: 즉시 변경TCSADRAIN
: 모든 아웃풋이 파일 디스크립터에 쓰이고 난 뒤 변경 (터미널 속성 변경이 아웃풋에 영향을 주는 경우 사용)TCSAFLUSH
: 모든 아웃풋이 파일 디스크립터에 쓰이고 난 뒤 변경 (받았지만 아직 쓰지 않은 인풋은 무시)TCSASOFT
: 이 값을 다른 속성에 or 연산하면 c_cflag
, c_ispee
, c_ospeed
필드가 무시됨termios_p
: 터미널의 정보를 세팅할 구조체 포인터0
, 실패 시 -1
리턴0
을 설정하면 터미널 연결을 종료함0
을 설정하면 아웃풋 스피드와 같은 값으로 설정됨https://www.gnu.org/software/termutils/manual/termcap-1.3/html_chapter/termcap_2.html
http://www.xevious7.com/linux/lpg_8_2_1.html
함수와 커서제어
[UNIX] termcap 라이브러리를 이용한 커서 제어
커서 제어를 위한 termcap(terminal capability) 라이브러리
termcap 라이브러리를 사용하려면 컴파일할 때
-Incurses
옵션을 줘야 함
terminfo database
터미널 정보를 가진 데이터베이스
터미널의 기능(capabilities)에 대한 정보들을 담고 있음
tgetent()
커서를 제어할 엔트리 설정
int tgetent(char *bp, const char *name);
bp
: 설정된 엔트리를 저장할 버퍼NULL
로 설정하면 된다고 함NULL
전달 시 알아서 필요한 공간을 할당하는데, 나중에 tgetent()
를 다시 호출하면 메모리를 해제함name
: 엔트리를 설정할 장치 이름1
0
-1
getenv()
로TERM
을 조회하면 현재 장치 이름을 받을 수 있다고 함
아래의 함수들을 사용할 때 터미널 정보를 넘겨줄 필요는 없음. 가장 최근에 tgetent()
를 실행한 정보가 자동으로 사용됨
Capability values can be numeric, boolean (capability is either present or absent) or strings. Any particular capability always has the same value type; for example,
co
always has a numeric value, whileam
(automatic wrap at margin) is always a flag, andcm
(cursor motion command) always has a string value. The documentation of each capability says which type of value it has.
👉 사용할 기능(capability)에 따라 아래의tget
함수를 골라 쓰면 되는 듯
tgetflag()
,tgetnum()
,tgetstr()
에 전달된 문자열의 첫 두 문자만 유효하게 해석된다고 함
tgetflag()
기능에 대한 불리언 값 받기
int tgetflag(char *id);
id
: 기능 코드1
리턴0
리턴tgetnum()
기능에 대한 숫자 값 받기
int tgetnum(char *id);
id
: 기능 코드-1
리턴tgetstr()
기능에 대한 문자열 값 받기
char *tgetstr(char *id, char **area);
id
: 기능 코드area
: 얻은 문자열의 복사본을 저장할 버퍼 (Unix 버전인 경우에는 유일한 방법이라 필수)NULL
을 전달하면 필요한 만큼 공간을 할당해서 리턴 (나중에 free()
해야 함)NULL
리턴tputs()
로 실행tgoto()
커서 움직임을 제어
char *tgoto(const char *cap, int col, int row);
cap
: 기능 코드cm
(cursor motion)에 사용BC
(backspace if not bs)나 UP
(cursor up)에도 쓴다고 함col
: 열row
: 행NULL
리턴(0, 0)
tgoto()
를 호출할 때마다 재활용tputs()
기능 실행
int tputs(const char *str, int affcnt, int (*putc)(int));
str
: tgetstr()
나 tgoto()
의 리턴값affcnt
: 영향을 받을 라인 수 (현재 라인만 해당하면 1
)putc
: 한 번에 하나의 문자를 받아서 출력하는 putchar()
같은 함수1
str
이 NULL
이면 0
-1
putc()
의 리턴값은 무시됨