#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdbool.h>
#include <sys/wait.h>
typedef bool command_func(int argc, char *argv[]);
struct command {
char *name;
char *desc;
command_func *func;
};
command_func command_cd, command_exit, command_help;
struct command builtin_commnads [] = {
{ "cd", "change directory", command_cd },
{ "exit", "exit this shell", command_exit },
{ "quit", "quit tihs shell", command_exit },
{ "help", "show this help", command_help },
{ "?", "show this help", command_help }
};
const int builtin_commnads_size = sizeof(builtin_commnads)
/ sizeof(struct command);
bool command_cd(int argc, char *argv[])
{
if (argc == 1) {
chdir(getenv("HOME"));
} else if (argc == 2) {
if (chdir(argv[1]))
printf("No directory\n");
} else printf("USAGE: cd [dir]\n");
return true;
}
bool command_exit(int argc, char *argv[])
{
return false;
}
bool command_help(int argc, char *argv[])
{
for (int i = 0; i < builtin_commnads_size; i++)
printf("%-10s: %s\n", builtin_commnads[i].name,
builtin_commnads[i].desc);
return true;
}
int tokenize(char *buf, char *delims, char *tokens[], int maxTokens)
{
int count = 0;
char *token;
token = strtok(buf, delims);
while (token != NULL && count < maxTokens) {
tokens[count] = token;
count++;
token = strtok(NULL, delims);
}
tokens[count] = NULL;
return count;
}
bool run(char *line)
{
const char delims[] = " \r\n\t";
char *tokens[128];
int token_count;
int status;
pid_t child;
token_count = tokenize(line, (char *) delims,
tokens, sizeof(tokens) / sizeof(char *));
if (token_count == 0)
return true;
for (int i = 0; i < builtin_commnads_size; i++) {
if (strcmp(builtin_commnads[i].name, tokens[0]) == 0)
return builtin_commnads[i].func(token_count, tokens);
}
child = fork();
if (child == 0) {
execvp(tokens[0], tokens);
printf("No such file\n");
} else if (child < 0) {
printf("Failed to fork()\n");
exit(EXIT_FAILURE);
} else wait(&status);
return true;
}
int main(void)
{
char line[1024];
while (true) {
printf("%s $ ", get_current_dir_name());
fgets(line, sizeof(line) - 1, stdin);
if (run(line) == false)
break;
}
return 0;
}
크게 어려운 내용은 없고, run()
함수에 핵심적인 코드가 담겨있다. shell
프로그램이 입력받은 명령어를 확인하고, 해당 명령어가 builtin_commands
경우 미리 정의된 동작을 실행, 존재하지 않는 명령어의 경우 execvp()
함수를 통해 실행한다.
execvp
함수의 첫 번째 인자로 전달된 filename
을 찾지 못한다면 PATH
환경변수를 조회하여 프로그램을 실행하므로 shell
프로그램과 큰 차이없이 동작하게 된다.
[책] 리눅스 커널 내부구조 (백승제, 최종무 저)
[책] 유닉스 고급 프로그래밍 3판 (리처드 스티븐스, 스티븐스 레이고 지음, 류광 옮김)