Pipex - Bonus
Pipex - Bonus
개요
mandatory에서는 쉘 명령어 두 개만 구현하면 됐습니다. bonus에서는 다중 파이프와 here_doc 구현입니다. 그렇다면 mandatory와는 다르게 표준 입력과 파일 간의 데이터 통신, 다중 프로세스 구현이 되겠군요.
ft_printf 과제와 비슷하게 보너스를 구현하면 mandatory가 아주 쉽게 느껴질 정도로 보너스 난이도가 어렵습니다. 다중 파이프와 입출력 리디렉션을 구현해 봅시다.
목표
Handle multiple pipes :다중 파이프 구현하기:
$> ./pipex file1 cmd1 cmd2 cmd3 ... cmdn file2
Must be equivalent to :의 결과값은 다음과 같아야 합니다:
< file1 cmd1 | cmd2 | cmd3 ... | cmdn > file2
Support « and » when the first parameter is "here_doc"첫 번째 인자값이 "here_doc" 일 경우 <<와 >> 구현하기
$> ./pipex here_doc LIMITER cmd cmd1 file
Must be equivalent to:의 결과값은 다음과 같아야 합니다:
cmd << LIMITER | cmd1 >> file
구현 전 사전 지식
1. here_doc
2. 다중 프로세스
1. here_doc
- here_doc은 here document를 뜻하며 쉘 스크립트에서 사용되는 특별한 문법입니다. 여러 줄에 걸쳐 입력을 제공하여 커맨드 라인을 형성할 수 있습니다.
- 스크립트 내에서 파일이나 명령어의 입력을 지정할 때 사용됩니다.
- 리디렉션 기호
- < (입력 리디렉션): '<' 기호는 파일로부터 입력을 받아 명령어에 전달하는 데 사용됩니다. 일반적으로 명령어가 표준 입력 대신 파일의 내용을 사용하도록 지정할 때 사용됩니다.
- > (출력 리디렉션): '>' 기호는 명령어의 출력을 파일로 리디렉션하는 데 사용됩니다. 명령어의 결과를 파일에 저장하거나 덮어쓸 수 있습니다.
- >> (Append 리디렉션): >>는 표준 출력을 파일에 추가하는 데 사용됩니다. 즉, 기존 파일 내용의 끝에 새로운 출력 내용을 추가합니다.
- << (Here 문서 리디렉션): <<는 Here 문서를 생성하기 위해 사용됩니다. Here 문서는 여러 줄의 입력을 제공하고자 할 때 유용하게 활용됩니다. Here 문서는 입력을 시작할 데 사용할 분리 구분자(delimiter)를 지정하고, 해당 구분자가 나타날 때까지의 모든 줄을 입력 데이터로 취급합니다.
2. 다중 프로세스
- mandatory와 크게 다르지 않습니다. infile > cmd1 > cmd2 > outfile 모습에서 cmd끼리의 데이터 통신이 늘어났을 뿐입니다.
구현
here_doc code
void execute_hd(t_cmd *info)
{
char *line;
while (1)
{
write(1, "pipe heredoc> ", 15);
line = get_next_line(0);
if (!ft_strncmp(info->limiter, line, ft_strlen(line) + 1))
{
free(line);
exit(0);
}
write(info->hd_fd[1], line, ft_strlen(line));
write(info->hd_fd[1], "\n", 1);
free(line);
}
}
here_doc 코드는 다음과 같습니다. 일단 here_doc을 위한 프로세스를 생성하고, get_next_line 함수를 통하여 표준 입력모드를 실행한 다음, 입력이 limiter 문자열과 일치할 때까지 입력을 받습니다. 이때 입력된 데이터는 어딘가에 저장되어 있어야 하므로 here_doc을 위한 파이프를 생성하고 데이터를 저장합니다. 굳이 here_doc의 입력을 받으려고 프로세스를 하나 생성할 필요가 있었겠느냐는 생각이 듭니다만, 함수가 하나의 작업을 하듯, 과제에서 파이프를 사용할 때마다 파일 디스크립터를 바꾸다 보니, 입출력 작업마다 프로세스를 하나 생성하는 습관이 생겼습니다.
pipex 평가를 진행했을 때, here_doc 입력 데이터를 처리하는 방법에 대해서는 임시 파일을 생성하는 방법도 있습니다. 위와 같이 파이프에 데이터를 넣지 않고 임시 파일을 만들어 그 파일에 데이터를 저장하고 표준 출력이 진행될 시점에 파일을 읽는 방법입니다. 처리가 끝났으면 파일을 지워 위의 방식이든 원본의 동작 방식이든 같은 결과를 만들 수 있습니다. *이는 허용된 외부 함수에서 unlink 함수가 존재하는 이유라고 생각합니다. 따라서 이 방법이 더 옳은 방법이라고 생각합니다.
multi_pipe code
void c_process_mp(t_cmd *info, char *av)
{
pid_t child;
if (pipe(info->mp_fd) == -1)
print_error("pipe", info, 1);
child = fork();
if (child == -1)
print_error("fork", info, 1);
if (child == 0)
{
close(info->mp_fd[0]);
dup2(info->mp_fd[1], 1);
close(info->mp_fd[1]);
execute_cmdline(info, av);
}
else
{
close(info->mp_fd[1]);
dup2(info->mp_fd[0], 0);
close(info->mp_fd[0]);
}
}
void p_process_mp(t_cmd *info, char *av, int i, int ac)
{
pid_t child;
int status;
pid_t temp;
child = fork();
if (child == -1)
print_error("fork", info, 1);
if (child == 0)
{
dup2(info->file[1], 1);
close(info->file[1]);
execute_cmdline(info, av);
}
open_close(info);
while (i < ac - 1)
{
temp = wait(&status);
if (temp == -1)
print_error("wait", info, 1);
i++;
}
exit(0);
}
커맨드 라인의 명령어 - 1 만큼 c_process_mp 함수를 호출하여 실행합니다. 이들은 표준입출력과 파이프의 fd를 계속 바꿔가며 데이터를 넘겨야 하므로, 실행마다 파이프를 새로 생성하며 이전 파이프에서 가져온 데이터를 다음 파이프로 넘기고... 생성하고... 넘기고... 를 반복합니다.
처음 실행한 명령어(함수 호출)가 파이프를 생성해 데이터를 집어넣고, 다음 명령어(동일한 함수 재 호출)가 파이프를 또 생성해 이전 데이터를 넘기고를 반복하는 것입니다. 마지막 명령어는 outfile로 들어갈 것이므로, 파이프를 생성하지 않아서 따로 다음 함수로 빼두었습니다.
p_process_mp 함수는 outfile로 데이터를 보낼 자식 프로세스를 생성하여 실행하고, 자식 프로세스들의 종료를 확인한 뒤, 리소스를 회수하고 프로그램을 종료합니다. wait 함수는 최근 종료된 자식 프로세스를 확인하므로, 생성한 자식프로세스 수만큼 반복 실행하면 모든 자식 프로세스의 종료를 확인할 수 있습니다.
동료 평가
mandatory와 동일합니다.
마치며
minishell을 위해서 반드시 해야한다고 마음먹고 도전하셨다면 끝까지 해내시길 바랍니다. 이제 minishell을 시작하게 될 텐데, 많은 도움이 될 거라고 믿고 있습니다.