본문 바로가기
42Seoul

Minishell - Bonus

by millar 2023. 12. 15.

And, Or, Subshell, Wildcard

 

개요

리뷰가 늦었습니다. 과제는 밀어놓고 개인적인 일로 바빠서 리뷰를 못하고 있었네요. minishell 과제를 끝내자마자 그래픽 과제 miniRT와 philosopher과제를 동시에 하다 보니 정신이 너무 없었습니다.

 

 이번 내용은 보너스에 관한 내용입니다. And, Or 연산자와 서브쉘 그리고 와일드카드에 대해서 알아보겠습니다. 터미널에서 쉘 명령어를 자주 사용하므로, 해당 명령어를 접했을 수도 있습니다.

 

목표

Your program has to implement:
프로그램이 다음을 구현해야 합니다.

• && and || with parenthesis for priorities.
→ 우선 순위를 위해 괄호와 함께 &&와 ||를 구현.
• Wildcards * should work for the current working directory.
→ 와일드 카드 *은 현재 작업 디렉토리에서 작동해야 합니다.

구현 전 사전 지식

1. And, Or

2. Subshell

3. Wildcard


 

1. And, Or

&& (AND 연산자): 이 연산자는 앞의 명령이 성공적으로 실행된 경우에만 다음 명령을 실행합니다. 즉, 첫 번째 명령이 성공하면(종료 코드가 0이면) 두 번째 명령이 실행됩니다. 만약 첫 번째 명령이 실패하면(종료 코드가 0이 아니면) 두 번째 명령은 실행되지 않습니다.

 

|| (OR 연산자): 이 연산자는 앞의 명령이 실패한 경우에만 다음 명령을 실행합니다. 즉, 첫 번째 명령이 실패하면(종료 코드가 0이 아니면) 두 번째 명령이 실행됩니다. 첫 번째 명령이 성공하면(종료 코드가 0이면) 두 번째 명령은 실행되지 않습니다.

 

병렬로 실행되는 파이프와 달리 순차적으로 명령어가 실행되므로, n번째 명령어는 n - 1번째 명령어의 종료 코드 영향을 받습니다.

2. Subshell

Subshell은 현재 쉘 프로세스 내에서 새로운 쉘 프로세스를 생성하는 메커니즘을 나타냅니다. 이는 새로운 환경을 만들고 명령을 실행하는 데 사용됩니다. Subshell은 주로 괄호를 사용하여 생성됩니다.

3. Wildcard

wildcard는 특정 패턴에 일치하는 파일이나 디렉토리의 목록을 지정하는 데 사용되는 문자입니다. Wildcard를 사용하면 여러 파일이나 디렉토리를 지정하거나 특정 패턴에 맞는 파일을 찾을 수 있습니다.


구현

구현 예시 1. And, Or

void	run_andif_list(t_tree_node *node, char ***env, t_tree_node *root)
{
	t_tree_node	*child;

	child = node->child_list;
	if (!g_exit_status)
		run_pipeline(child, env, LIST, root);
	child = child->next;
	if (child)
	{
		if (child->symbol == AND_IF)
			run_andif_list(child->next, env, root);
		else if (child->symbol == OR_IF)
			run_orif_list(child->next, env, root);
	}
}

void	run_orif_list(t_tree_node *node, char ***env, t_tree_node *root)
{
	t_tree_node	*child;

	child = node->child_list;
	if (g_exit_status)
		run_pipeline(child, env, LIST, root);
	child = child->next;
	if (child)
	{
		if (child->symbol == AND_IF)
			run_andif_list(child->next, env, root);
		else if (child->symbol == OR_IF)
			run_orif_list(child->next, env, root);
	}
}

 

 

And와 Or은 List의 sibling node입니다. 따라서 이들은 반드시 하나의 파이프 라인을 가지며 파이프 라인을 실행하고 '형제 노드가 남아 있다면' 파이프라인(실행 명령어)의 종료 코드를 확인하고 &&, ||를 각각 실행할 것인지 아닌지 판단하여 다시 list 계층부터 실행을 이어 나가면 되겠습니다.

 

구현 예시 2. Subshell

void	run_command_pipe(t_tree_node *node, char ***env, t_tree_node *root)
{
	t_tree_node	*child;
	pid_t		sub_pro;

	child = node->child_list;
	if (child->symbol == SIMPLE_COMMAND)
		run_simple_command(child, env, root);
	else
	{
		sub_pro = fork();
		if (sub_pro == -1)
			system_call_error();
		if (sub_pro == 0)
		{
			if (run_subshell_redir(child->next, env) == 0)
				run_list(child->child_list->next, env, root);
			exit(g_exit_status);
		}
		else
			wait_record_status(sub_pro);
	}
}

 

Command 계층까지 도달하면 Subshell을 만날 수 있습니다. 자식 노드가 Simple_Command이거나 Subshell이기 때문에 이를 확인하고, Subshell일 경우 프로세스를 생성하여 괄호 안에 있는 실행 단위를, 그 프로세스에서 실행해 주시면 되겠습니다.

더보기
정말 Subshell이 현재의 쉘 프로세스에서 새로운 쉘 프로세스를 생성하여 실행될까?

 

지금 터미널을 2개 창으로 띄워놓고 yes, (yes) 명령어를 각각 실행한 뒤, 다른 하나의 터미널에서 ps를 실행해 봅시다. bash의 매뉴얼대로라면, yes는 프로세스 yes만 존재할 것이고 (yes)는 무언가 하나의 프로세스가 더 감지될 것입니다.

 

라는 생각으로 시도해 보았지만, Subshell의 프로세스가 따로 ps 명령어로 탐지되지는 않았습니다.

 

뭔가 이대로 찾지 못한 것이 아쉬워서 다른 방법으로 Subshell의 프로세스 흔적을 찾으려고 했습니다.

 

두 개의 bash터미널에 (sleep 5 && sleep 2)와 같이 입력했을 때, ps가 3개의 bash를 표시함을 확인할 수 있었습니다. 2개의 bash가 표시되어야 하는데 하나의 bash는 Subshell을 의미한다고 추측할 수 있겠습니다.

 

Subshell 내에서 환경변수를 선언한 것이 상위 프로세스에서 적용되지 않는다는 점과 (exit)과 같은 명령어 또한 부모 프로세스가 종료되지 않았기 때문에 따로 출력 메시지와 프로세스 종료가 이루어지지 않는다는 점에서 bash와 차이가 있습니다.

 

따라서, Subshell을 fork()함수로 구현하는 것이 올바른 방법이라고 판단했습니다.

구현 방향 3. Wildcard

 

Wildcard는 구현 예시가 아닌, 구현 방향으로 서술하겠습니다. 그 이유는 구현 방법이 여러 가지가 될 수 있기 때문입니다. 그래서 구현에 있어 길을 헤매지 않도록 내용을 단축할 수 있게 저희가 고려한 사항들을 나열해 보려고 합니다.

 

wildcard기호 *은 "* 문자 자리에 어떤 문자열이 와도 된다. 심지어 안 와도 된다."라고 생각하면 좋습니다.

 

  1. wildcard(*)은 숨김 파일을 포함하지 않습니다.
    1. 기본적으로 숨김 파일이 아닌 파일과 디렉토리를 지정합니다.
    2. 숨김 파일이나 디렉토리를 직접 지정하려면 .* 과같이 wildcard 앞에 .을 붙여 숨김 파일이라는 것을 명시해야 합니다.
    3. readdir 함수를 사용하여 구현해야 할 텐데, 이때 숨김 파일을 제외할 수 있게 합시다.
  2. wildcard기호 *은 다수로 사용이 가능합니다.
    1. *을 ***...***와 같이 이어 붙여 쓰는 것은 *과 동일합니다.
    2. a*b*c와 같은 입력도 가능합니다. a와 b, b와 c사이에 어떤 문자열이든 올 수 있습니다. 안와도 됩니다.
    3. *abc, abc* 각각은 abc를 포함합니다. 2번 내용과 비슷합니다.
  3. wildcard기호 *은 single quote와 double quote에 감싸져 있다면, 문자 기호의 역할을 합니다. (*를 해석할때, quote에 있다면 문자 기호로 해석합시다)
  4. ls, cd, rm 등 인자를 받는 명령어들이 wildcard(*)를 인자로 받을 수 있습니다.* 이는 구현의 고려 사항이 아닙니다! wildcard와 built - in을 제대로 구현했다면, 올바르게 작동할 것입니다. 평가를 위해 알고 있어야 합니다.* wildcard로 선택될 파일, 디렉토리가 1개 혹은 2개 이상임에 따라 명령어가 다른 결과를 보입니다. 직접 해보세요!
  5. Redirection은 wildcard 기호를 받을 수 있습니다.
    1. 4번에서 언급했던 것처럼 파일이 1개라면 wildcard가 하나의 파일을 지정할 것이고 이것이 redirection으로 동작할 수 있습니다.
    2. 하지만 2개 이상이라면 redirection은 2개의 파일을 동시에 표준 입출력으로 지정할 수 없으므로 ambiguous 에러를 뱉습니다.
    3. 이 조건은 bash에서만 해당합니다. zsh은 다수의 redirection이 가능합니다.
  6. Redirection의 일종인 Heredoc은 wildcard 기호를 받을 수 있을까요?

될리가 없죠


마치며

가장 큰 첫 번째 고비 minishell을 멋지게 끝내셨습니다.

'42Seoul' 카테고리의 다른 글

Philosophers - Bonus  (1) 2023.12.17
Philosophers - Mandatory  (1) 2023.12.17
Minishell - Signal & (Heredoc)  (2) 2023.11.09
Minishell - Built-in  (1) 2023.11.09
Minishell - LCRS hierarchy & BNF production rules  (0) 2023.11.06

댓글