3. minishell

zinnnn37·2023년 2월 16일
0

42 SEOUL

목록 보기
5/9

3.0. Allowed functions

	# <readline/readline.h> 헤더 설치
	brew install readline	# readline 헤더 설치
    brew info readline		# 설치 확인 & 경로 확인

    # makefile로 컴파일 시 아래 플래그 추가
    -lreadline -Lreadline/lib -Ireadline/include

	# 나같은 경우는 경로가 /Users/42id/.brew/opt/readline/lib
    # 환경에 따라 다 다르게 나오는 것으로 보인다
	RL_LIB		=	-lreadline -L${HOME}/.brew/opt/readline/lib
	RL_INC		=	-I${HOME}/.brew/opt/readline/include
    
    $(NAME)		:	$(OBJS)
		$(CC) $(CFLAGS) -o $(NAME) $(OBJS) $(RL_LIB)
    
    %.o			:	%c
		$(CC) $(CFLAGS) -I$(INCLUDE) $(RL_INC) -c $< -o $@

⚠️ readline 설치 후

export LDFLAGS="-L/Users/42id/.brew/opt/readline/lib"
export CPPFLAGS="-I/Users/42id/.brew/opt/readline/include"

두 명령을 실행해 주어야 프로그램이 새롭게 설치한 readline.h에 제대로 접근함 ⚠️
-> 하지 않는 경우 rl_replace_line() 함수를 사용하지 못 함
-> Mac에 기본적으로 깔려있는 readline.h에는 존재하지 않는 함수이기 때문


3.0.0. readline()

	#include <readline/readline.h>
	char	*readline(const char *str);
  • 프롬프트에 문장을 입력 받고 입력 받은 문장을 저장하는 용도로 사용된다.
  • 동적 할당을 하므로 반드시 free를 해주어야 한다.
  • 입력 받은 문자열을 저장하고 그 메모리 주소를 반환한다.
  • 공백 문자만 입력한 후 개행을 입력한 경우 NULL을 반환한다
  • EOF를 만나는 경우
    1. EOF 전에 처리할 문자가 있는 경우, EOF를 개행문자처럼 처리하여 문자열을 반환
    2. 문자가 없는 경우 NULL을 반환

입력 받은 문자열을 저장하고 그 메모리 주소를 반환한다
EOF를 만나는 경우 NULL return
프롬프트에 문장 입력받고 문자열 저장하는 용도
반드시 free 해야 함!

  • \n 앞 까지 저장
	char *s = readline("prompt: ");
    // 프롬프트에 prompt: 가 출력되고 그 뒤로 입력된 문장이 s에 저장

3.0.1. rl_on_new_line

	#include <readline/readline.h>
	int	rl_on_new_line(void);
  • update 관련 함수들에게 커서가 다음 줄로 이동했다는 것을 알려주는 함수. 개행문자 출력 이후에 사용된다.
  • 실행 성공 시 0 반환, 실패 시 -1 반환

3.0.2.rl_replace_line

	#include <readline/readline.h>
	void	rl_replace_line(const char *text, int clear_undo);
  • rl_line_buffer에 입력받은 내용을 text로 대체한다.
  • clear_undo는 내부적으로 유지하고 있는 undo_list를 초기화할 지 그 여부를 결정하는 값이다. 값이 0이면 초기화하지 않고, 다른 값인 경우 초기화한다.

3.0.3. rl_redisplay

	#include <readline/readline.h>
	void	rl_redisplay(void);
  • 사용자가 입력하여 유지 중인 rl_line_buffer의 값을 프롬프트와 함께 출력한다. 이 때, 프롬프트로 readline()에서 입력된 문자열이 사용된다.
  • 주로 signal을 받을 때 사용된다.

3.0.4. add_history

	#include <readline/history.h>
	int	add_history(const char *);
  • readline()을 통해 사용자가 입력했던 문자열을 저장하는 함수이다.
  • 프롬프트가 열린 상태에서 키보드 방향키 위, 아래를 통해 이때까지 프롬프트에 입력한 문자열을 불러올 수 있다.
  • 함수 실행 성공 시 0 반환, 실패 시 -1 반환
#include "../include/minishell.h"

int	main(int ac, char **av)
{
	char	*cmd;

	(void)av;
	if (ac != 1)
		return (FAILURE);
	while (TRUE)
	{
		cmd = readline("minishell_$ ");
		if (!cmd)
			break;
		printf("%s\n", cmd);
		add_history(cmd); // 추가 시 상하 방향키로 명령어 히스토리 불러오기 가능
		free(cmd);
	}
	return (SUCCESS);
}

3.0.5. fork

  • 자식 프로세스를 만드는 함수
  • 함수 실행 성공 시 브모 프로세스에겐 자식 프로세스의 pid 반환, 자식 프로세스에겐 0 반환, 실패 시 0 반환
int	main(void)
{
	pid_t	pid;

	pid = fork();
	if (pid > 0)
		printf("Parent ID : %d\n", getpid());
	else if (pid == 0)
		printf("Child ID : %d\n", getpid());
	return (SUCCESS);
}

Parent ID : 88613
Child ID : 88614


3.0.6. wait

	#include <sys/wait.h>
    pid_t	wait(int *statloc);
  • statloc의 주소를 인자로 넣어 자식 프로세스의 상태를 받을 수 있다.
  • 자식 프로세스가 정상적으로 종료된 경우 pid 반환, 실패 시 -1 반환

3.0.7. waitpid

	#include <sys/wait.h>
    pid_t waitpid(pid_t pid, int *statloc, int options);
  • 자식 프로세스를 기다릴 때 사용하는 함수, 즉, 자식 프로세스의 종료 상태를 회수할 때 사용한다.

  • 그러나 wait()과 다르게 자식 프로세스가 종료될 때까지 차단되는 것을 원하지 않는 경우 옵션을 이용하여 차단을 방지할 수 있다.

  • 함수 실행 성공 시 PID 반환, 실패 시 -1 반환

  • pid
    1. pid == -1
    : 임의의 자식 프로세스를 기다림

    1. pid > 0
      : pid와 동일한 자식 프로세스를 기다림
    2. pid < -1
      : 프로세스 그룹 ID가 pid의 절댓값과 같은 자식 프로세스를 기다림
    3. pid == 0
      : waitpid()를 호출한 프로세스의 프로세스 그룹 ID와 같은 프로세스 그룹 ID를 가진 프로세스를 기다림
  • statloc
    : 자식 프로세스 상태에 따라 달라짐
  • options
    1. WCONTINUED
    : 중단되었다 재개된 자식 프로세스의 상태를 받음
    1. WNOHANG
      : 종료한 자식이 없는 경우 즉시 반환
    2. WUNTRACED
      : 중단된 자식 프로세스의 상태를 받음
    3. 0
      : wait()와 동일하게 작동


  • 세 번째 인자에 0 입력 시
int	main(void)
{
	pid_t	pid;
	int		status;
	int		ret;

	pid = fork();
	if (pid > 0)
	{
		printf("Parent ID : %d\n", getpid());
		ret = waitpid(pid, &status, 0);
		printf("Parent killed\n");
	}
	else if (pid == 0)
	{
		printf("Child ID : %d\n", getpid());
		usleep(100);
		printf("Child killed\n");
	}
	return (SUCCESS);
}

Parent ID : 29162
Child ID : 29163
Child killed
Parent killed

-> 자식 프로세스가 종료된 후 부모 프로세스가 종료

  • WNOHANG 입력 시
int	main(void)
{
	pid_t	pid;
	int		status;
	int		ret;

	pid = fork();
	if (pid > 0)
	{
		printf("Parent ID : %d\n", getpid());
		ret = waitpid(pid, &status, WNOHANG);
		printf("Parent killed\n");
	}
	else if (pid == 0)
	{
		printf("Child ID : %d\n", getpid());
		usleep(100);
		printf("Child killed\n");
	}
	return (SUCCESS);
}

Parent ID : 29326
Parent killed
Child ID : 29327
Child killed

-> usleep(100)으로 인해 자식 프로세스가 종료되지 않아 부모 프로세스부터 종료됨


3.0.8. wait3

	#include <sys/wait.h>
    pid_t	waitpid(pid_t pid, int *staticloc, int options);
  • 자식 프로세스가 종료되는 것을 기다리며, 종료된 프로세스의 상태와 자원 사용량을 알려주는 함수이다.
  • wait()와 같지만 옵션과 pid를 지정하여 사용자가 원하는 대로 제어 가능하다.
  • waitpid(-1, status, options)와 동일하게 동작

3.0.9. wait4

	#include <sys/wait.h>
	pid_t wait4(pid_t pid, int *statloc, int options, struct rusage *rusage);
  • rusage: 자식 프로세스의 리소스 사용량에 대한 정보가 담긴다.
  • waitpid()와 동일하게 작동

wait3()wait4()waitpid()가 생긴 후로 구식함수가 되었다


3.0.10. signal

	#include <signal.h>
    void	(*signal(int sig, void (*func)(int)))(int);
  • 신호를 처리하는 함수

  • 성공 시 이전 핸들러를 반환, 실패 시 SIG_ERR를 반환

  • sig
    1. SIGABRT : 비정상적 종료
    2. SIGFPE : 부동 소수점 오류
    3. SIGILL : 잘못된 명령
    4. SIGINT : 터미널 인터럽트 신호(ctrl + C)
    5. SIGSEGV : 잘못된 메모리 참조
    6. SIGTERM : 종료 신호

  • 함수 포인터
    1. SIG_DFL : 신호에 대한 기본 작업으로 처리
    2. SIG_IGN : 신호 무시
    3. 함수 이름 : 지정 함수 호출


3.0.11. kill

	#include <signal.h>
    int	kill(pid_t pid, int sig);
  • 프로세스에 시그널을 전송하는 함수

  • 성공 시 0, 실패 시 -1 반환

  • pid

  1. pid > 0 : 지정한 프로세스 ID에만 시그널 전송
  2. pid == 0 : 함수를 호출하는 프로세스와 같은 그룹에 있는 모든 프로세스에 시그널 전송
  3. pid == -1 : 함수를 호출하는 프로세스가 전송할 수 있는 권한을 가진 모든 프로세스에 시그널 전송
  4. pid < -1 : pid의 절댓값 프로세스 그룹에 속하는 모든 프로세스에 시그널 전송
int	main(void)
{
	pid_t	pid;
	int		status;
	int		ret;

	pid = fork();
	if (pid > 0)
	{
		printf("Parnet ID : %d\n", getpid());
		ret = waitpid(pid, &status, 0);
		printf("Parent killed\n");
	}
	else if (pid == 0)
	{
		printf("Child ID : %d\n", getpid());
		usleep(100);
		kill(pid, SIGKILL); // 부모가 먼저 죽는다
		printf("Child killed\n");
	}
	return (SUCCESS);
}

Parnet ID : 88149
Child ID : 88150
[1] 88149 killed ./minishell


3.0.12. getcwd

	#include <unistd.h>
    char	*getcwd(char *buf, size_t size);
  • 현재 작업 경로를 가져오는 함수

  • 성공 시 경로, 실패 시 NULL 리턴. 실패 시 errno에 상세 오류 내용이 저장된다.

  • buf: 경로 저장 변수
    -> bufNULL인 경우 함수 내에서 malloc 후 리턴하므로 메모리 해제가 필수적이다.

  • size: 버퍼에 할당된 바이트 수

int	main(void)
{
	char	*pwd;

	pwd = NULL;
	printf("%s\n", getcwd(pwd, 0));
	free(pwd);
	return (SUCCESS);
}

/goinfre


3.0.13. chdir

	#include <unistd.h>
    int	chdir(const char *)
  • 현재 작업 디렉토리를 변경하는 함수
  • 성공 시 0, 실패 시 -1 반환
int	main(void)
{
	char	path[100];

	printf("%d\n", chdir(readline("Path to change: ")));
	printf("%s\n", getcwd(path, 100));
	return (SUCCESS);
}

성공:
Path to change: /goinfre/
0
/goinfre

실패:
Path to change: ~
-1
/goinfre


3.0.14. stat, lstat, fstat

	#include <sys/stat.h>
    int	stat(const char *path, struct stat *buf);
    int	lstat(const char *path, struct stat *buf);
    int	fstat(int fd, struct stat *buf);
  • 파일 정보를 읽어오는 함수

  • 심볼릭 링크 파일인 경우
    1. stat : 구조체에 원본 정보 채우기
    2. lstat : 심볼릭 링크 파일 정보 채우기

  • fstat의 경우 현재 열린 파일의 정보를 읽어 온다.
    -> open()으로 연 파일 등

  • 성공 시 0, 실패 시 -1 반환

  • path : 파일의 절대경로

  • buf : stat 구조체


	#include <unistd.h>
    int	unlink(const char *path);
  • 하드 링크를 생성하지 않은 파일은 disk space를 해제하여 바로 삭제한다.

  • 하드 링크가 있는 파일은 이름을 삭제하고 하드 링크가 참조하는 카운트를 1만큼 감소한다. 하드 링크의 카운트가 0이 되면 실제 파일의 내용이 저장되어 있는 disk space를 해제하여 완전히 삭제한다.

  • open()으로 파일을 연 경우 unlink()를 사용하면 정보는 삭제되더라도 disk space는 해제되지 않는다.

  • 성공 시 0, 실패 시 -1 반환

  • path : 상대 경로

int	main(void)
{
	int	ret;

	if ((ret = unlink(readline("File to delete: "))))
		printf("Deleted\n");
	else
		printf("Fail to delete\n");
}

File to delete: ~/goinfre/a
Deleted


3.0.16. execve

외 execve(file, null, null)이 않되나요..

	#include <unistd.h>
    int	execve(const char *filename, char *const argv[], char *const envp[]);
  • 현재 실행되는 프로그램의 기능을 없애고, filename 프로그램을 처음부터 실행

  • 성공 시 0, 실패 시 -1 반환

  • 함수 호출 후에 일어나는 프로세스의 변화
    1. signal 설정이 default로 변경됨
    2. opendir로 열린 directory streamclose
    -> 일반 file discriptorclose되지 않음
    3. atexit(3), on_exit(3)으로 등록된 exit handler가 해제됨
    4. 모든 스레드가 사라짐
    ...

  • filename
    - 교체할 실행 파일 or 명령어
    - 실행가능한 binary거나 shell이어야 함
    - 절대 경로나 상대 경로로 정확한 위치를 지정해야 함

  • argv
    - main(int ac, char **av);**av와 유사

  • envp
    - key=value 형식의 환경변수 문자열 배열리스트로 마지막은 NULL이어야 함

  • 만약 이미 설정된 환경변수를 사용하고자 하면 environ 전역변수 사용

extern char	**environ;

int	main(int ac, char **av)
{
	char	**new_av;
	char	cmd[] = "ls";
	int		idx;

	new_av = (char **)malloc(sizeof(char *) * (ac + 1));

	new_av[0] = cmd;
	for (idx = 1; idx < ac; idx++)
		new_av[idx] = av[idx];

	new_av[ac] = NULL;
	if (execve("/bin/ls", new_av, environ) == -1) // where ls로 위치 찾을 수 있음
	{
		fprintf(stderr, "error: %s\n", strerror(errno));
		return 1;
	}
	printf("ls 명령 실행으로 출력되지 않음\n");
	return 0;
}

Makefile include src README.md minishell


3.0.17. dup, dup2

	#include <unistd.h>
    int	dup(int fd);
    int	dup2(int fd, int fd2);
  • 파일을 복제하는 함수
  • dup()은 파일 복제 시 사용하지 않는 fd 중 가장 작은 번호가 자동으로 지정되고, dup2()는 사용자가 원하는 fd로 지정 가능하다. 만약 사용 중인 경우 해당 fd의 파일을 다은 후 지정한다.
  • 복제하고자 하는 파일의 fd를 얻어야 하므로 open하여 사용한다
int	main(void)
{
	int fd1, fd2, fd3;

	fd1 = open("test", O_RDONLY);
	fd2 = dup(fd1);
	fd3 = dup2(fd2, 5);
	printf("fd1: %d\nfd2: %d\nfd3: %d\n", fd1, fd2, fd3);
	close(fd1);
	close(fd2);
	close(fd3);
}

fd1: 3
fd2: 4
fd3: 5


3.0.18. pipe

	#include <unistd.h>
    int	pipe(int fd[2])
  • 파이프를 생성하여 만들어진 디스크립터를 할당한다.

  • 디스크립터를 얻으면 부모와 자식 프로세스가 사용할 수 있다.

  • fork()에 의해 복사가 되지 않는다

  • 성공 시 0, 실패 시 -1 반환

  • fd[2]
    - fd[0]: 파이프로부터 읽는 디스크립터
    - fd[1]: 파이프에 쓰는 디스크립터

  1. 자식 프로세스는 파이프의 읽는 쪽인 fd[0]을 닫고, 표준 출력이 fd[1]이 가리키는 스트림을 가리키도록 변경한다.
    -> 자식 프로세스가 표준 출력으로 보내는 모든 데이터가 파이프에 쓰인다.
  2. 이 경우, 부모 프로세스는 쓰지 않기 때문에 파이프의 쓰는 쪽인 fd[1]을 닫고, 프로세스의 표준 입력을 fd[0]이 가리키는 스트림으로 리다이렉션한다.

➜ 이렇게 자식 프로세스가 쓰는 모든 데이터를 부모 프로세스의 표준 입력을 통해 읽을 수 있다

int	main(int ac, char **av)
{
	int 	fd[2];
	pid_t	pid;

	if (pipe(fd) == -1)
	{
		fprintf(stderr, "pipe error: %s\n", strerror(errno));
		return (1);
	}
	pid = fork();
	if (pid == -1)
	{
		fprintf(stderr, "fork error: %s\n", strerror(errno));
		return (1);
	}
	if (pid == 0) // Child process
	{
		dup2(fd[1], 1);	// 표준 출력을 파이프의 쓰는 쪽으로 설정
		close(fd[0]);	// 자식은 파이프에서 읽지 않으므로 읽는 쪽을 닫는다
		int ste = execlp("ls", "ls", NULL);
		// execlp: PATH에 등록된 모든 디렉토리에 있는 프로그램을 실행하므로 프로그램 이름만 입력해도 실행이 됨
		// ps 중복: av[0]처럼 프로그램 전체 이름을 의미하기 때문
		// 즉 두 번째 인자인 ps부터 실행한다. (ps aux를 실행한 것)
		if (ste == -1)
		{
			fprintf(stderr, "run error: %s\n", strerror(errno));
			return (1);
		}
	}
	// 부모만 여기에 도달
	dup2(fd[0], 0);	// 표준 입력을 파이프의 읽는 쪽으로 리다이렉션
	close(fd[1]);	// 부모는 파이프에 쓰지 않으므로 쓰는 쪽을 닫는다
	char	line[255];
	while (fgets(line, sizeof(line), stdin) != 0)
		printf("%s", line);
	return (0);
}

Makefile
README.md
a.out
include
src
test
test.c

  • 부모와 자식 프로세스는 파이프로 통신한다.
  • pipe()는 파이프와 2개의 디스크립터를 생성하나.
  • 2개의 디스크립터는 파이프의 읽는 쪽과 쓰는 쪽이다.
  • 표준 입력과 출력을 파이프로 리다이렉션할 수 있다.
  • 부모와 자식 프로세서는 서로 다른 파이프의 반대쪽을 사용한다.
  • 파이프는 언제나 한 방향으로만 작동한다. 그러나 파이프를 두 개 만들어 하나는 부모가 자식에게, 하나는 자식이 부모에게 데이터를 보내도록 할 수 있다.

3.0.19. opendir, readdir, closedir

	#include <dirent.h>
    DIR				*opendir(const char *);
    struct dirent	*readdir(DIR *);
    int				closedir(DIR *);

	#define __DARWIN_STRUCT_DIRENTRY { \
    	__uint64_t  d_ino;      /* file number of entry */ \
		__uint64_t  d_seekoff;  /* seek offset (optional, used by servers) */ \
		__uint16_t  d_reclen;   /* length of this record */ \
		__uint16_t  d_namlen;   /* length of string in d_name */ \
		__uint8_t   d_type;     /* file type, see below */ \
		char      d_name[__DARWIN_MAXPATHLEN]; /* entry name (up to MAXPATHLEN bytes) */ \
	}
  • opendir()
    : 디렉토리 열기. 성공 시 DIR 포인터, 실패 시 NULL 반환
  • readdir()
    : 디렉토리 내부 목록 읽기. 성공 시 파일 혹은 디렉토리 정보, 실패 시 NULL 반환
  • closedir()
    : 디렉토리 닫기. 성공 시 0, 실패 시 -1 반환
int	main(void)
{
	DIR				*path = NULL;
	struct dirent	*file;

	if (!(path = opendir(readline("Dir path: "))))
		printf("No such path\n");
	while ((file = readdir(path)))
		printf("name: %s\n", file->d_name);
	closedir(path);
}

Dir path: .
name: .
name: ..
name: test
name: Makefile
name: include
name: README.md
name: a.out
name: test.c
name: src


3.0.20. isatty

	#include <unistd.h>
    int	isatty(int fd);
  • 파일 디스크립터가 터미널을 참조하는지 확인하는 함수
  • 터미널과 관련이 있다면 ttyname()을 이용하여 이와 연관된 파일 이름을 얻을 수 있다.
  • 터미널에 연결되어 있는 경우 1, 그렇지 않은 경우 0 반환
int	main(void)
{
	int fd;

	printf("%d\n", isatty(0));	// 표준입력은 터미널에 연결되어 있으므로 1을 출력한다.
	fd = open("test100", O_RDWR);
	printf("%d\n", isatty(fd));	// 파일은 터미널에 연결되어 있지 않으므로 0을 출력한다.
	close(fd);
	fd = open("/dev/ttyS0", O_RDONLY);
	if (fd < -1)
	{
		printf("open error\n");
		exit(0);
	}
	printf("%d\n", isatty(fd));
	close(fd);
	return (0);
}

1
0
0


3.0.21. ttyname

	#include <unistd.h>
    char	*ttyname(int fd);
  • 터미널 이름을 반환하는 함수
  • 성공 시 터미널 이름, 실패 시 NULL 반환
int	main(void)
{
	printf("%s\n", ttyname(0));
	printf("%s\n", ttyname(5));
	return (0);
}

/dev/ttys000
(null)


3.0.22. ttyslot

	#include <unistd.h>
    int	ttyslot(void);
  • 프로그램이 참조하는 터미널의 인덱스를 반환

3.0.23. ioctl

	#include <sys/ioctl.h>
    int	ioctl(int fd, unsigned long request, ...);
  • 디바이스를 제어하는 시스템 콜 함수.
  • 하드웨어를 제어하거나 상태 정보를 확인할 수 있다.

3.0.24. getenv

	#include <stdlib.h>
    char	*getenv(const char *name);
  • 환경변수를 불러오는 함수
int main(void)
{
	printf("%s\n", getenv(readline("env: ")));
}

env: HOME
/Users/42id


3.0.25. tcsetattr, tcgetattr

	#include <termios.h>
    int	tcsetattr(int fd, int action, const struct termios * termios_p)
    int	tcgetattr(int fd, struct termios *termios_p)
  • 터미널 파일 fd에 대한 터미널 설정하고 가져온다.

  • tcsetattr()
    : termios 구조체 초기화
    : termios_p의 내용대로 구조체 멤버를 설정한다

  • tcgetattr()
    : action 필드의 내용대로 설정한다
    - TCSANOW: 값을 즉시 변경
    - TCSADRAIN: 현재 출력이 완료되었을 때 값 변경
    - TCSAFLUSH: 현재 출력이 완료되었을 때 값을 변경하나, 현재 읽을 수 있으며 read 호출에서 아직 반환되지 않는 입력값을 버린다
    : 프로그램이 시작하기 전의 값으로 터미널 설정값을 복구하는 것이 중요함


3.0.26. tgetent, tgetflag, tgetnum, tgetstr

	#include <curses.h>
    #include <term.h>
    int		tgetent(char *bp, const char *name);
    int		tgetflag(char *id);
    int		tgetnum(char *id);
    char	*tgetstr(char *id, char **area);
  • tgetent(): 엔트리 이름을 가져옴. 성공 시 1, 해당 엔트리가 없는 경우 0, terminfo 데이터베이스를 찾을 수 없는 경우 -1 반환
  • tgetflag(): idboolean 정보를 가져옴. 실패 시 0 반환
  • tgetnum(): id의 숫자 정보를 가져옴. 실패 시 -1 반환
  • tgetstr(): id의 문자열 정보를 가져옴. 실패 시 NULL 반환
    -> tputs()를 이용해 반환된 문자열을 출력한다.
    -> 반환값은 area에도 저장된다.

3.0.27. tgoto, tputs

	#include <curses.h>
	#include <term.h>
	char *tgoto(const char *cap, int col, int row);
	int tputs(const char *str, int affcnt, int (*putc)(int));
  • tgoto(): 매개변수를 주어진 기능으로 인스턴스화. 출력은 tputs()로 전달된다.
  • tputs(): str에 패딩 정보를 적용하고 출력한다.
    - strterminfo 문자열 변수이거나 tparm(), tgetstr() 또는 tgoto()의 반환값이어야 한다.
    - affcnt는 적용되는 줄 수로, 적용되지 않는 경우 1로 설정한다.

➜ 정보가 많이 없다..🙂🙃🙂🙃


3.1. commands

3.1.0. echo

  1. -n 옵션(개행 삭제)
    : -nnnnn 이어도 작동 되며 -n -n 이런 식으로 중복되어도 작동 됨
    -> echo -nnnn hi : hi%
    -> echo -n -na hi : -na hi%
    -> echo -nnnn1 -n hi: -nnnn1 -n hi
    -> echo -nnnn -n hi: hi%

3.1.1. cd

경로 이동 명령어
절대경로 혹은 상대경로만 사용하도록 구현

3.1.2. pwd

현재 경로 출력

  • 뒤에 인자가 와도 현재 디렉토리의 경로 출력
    -> 옵션 붙어도 무시하고 경로 출력하게끔

3.1.3. export

3.1.4. unset

3.1.5. env

3.1.6. exit


3.2. redirection

리다이렉션 앞 뒤로 공백 없어도 작동 함 .. 주의할 것
공백 기준으로 split 한 후에 redirection 있는지 한 번 더 확인해야 할 듯

3.2.0. > output redirection

  • 출력을 파일의 내용으로 저장(덮어쓰기)
	command > file
    # command의 출력 결과를 file로 저장
  	
    > file
    # 터미널에 입력한 내용을 file에 저장
    # zsh는 터미널에 입력 받을 수 있는데 bash는 작동하지 않음, 대신 file 내용이 공백으로 업데이트됨

⚠️ ls > file을 할 때 file이 존재하지 않는 경우 file부터 만들고 ls의 결과를 file에 저장(ls 명령 실행 결과에 file도 포함이 됨) ⚠️

3.2.1. >> output redirection(append)

  • 출력을 파일의 내용으로 저장(이어쓰기)
	command >> file
    # command의 출력 결과를 file 맨 뒤에 이어쓰기
    
    >> file
    # 아무 동작하지 않음(공백을 맨 뒤에 붙이는 것이기 때문)

3.2.2. < input redirection

  • 파일의 내용을 입력으로 받기
	< file
    # 아무 동작 하지 않음

3.2.3. << heredoc

  • keyword가 나올때까지 입력받음
	<< keyword
    # keyword가 나올 때까지 입력만 받고 keyword 나오면 입력받기 종료

3.3. quotes

닫히지 않은 따옴표는 다룰 필요 없음

3.3.0. "double quote"

  • 환경변수 그대로 출력
	echo "${HOME}"
    echo "'${HOME}'"

/Users/42id
'/Users/42id'

3.3.1. 'single quote'

  • 환경변수 출력 X
	echo '${HOME}'
    echo '"${HOME}"'

${HOME}
"${HOME}"


3.4. signal

^C 없앨 때 tcsetattr 함수 사용 ?!

3.3.0. ctrl + c

	 SIGINT

새로운 줄에 새로운 프롬프트 생성
초기 설정은 minishell 종료

3.3.1. ctrl + d

minishell 종료
초기 설정은 새로운 줄
bash는 logout

  • non-canonical mode의 터미널 사용
    -> termios 구조체를 통해 tcsetattr, tcgetattr 등을 사용하여 제어

3.3.2. ctrl + \

	SIGQUIT

아무런 동작도 하지 않아야함
아무 설정도 하지 않으면 minishell quit
([1] PID quit ./minishell)
bash는 아무 동작 x


ref

unlink: https://www.it-note.kr/177

pipe: https://nomad-programmer.tistory.com/110

tty: https://powergi.tistory.com/entry/terminal-%ED%86%B5%EC%8B%A0

isatty: https://www.joinc.co.kr/w/man/3/isatty

termios: https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=y851004&logNo=20063499242

26, 27: https://linux.die.net/man/3/tgetent

0개의 댓글