1. 实现思路
考虑下面这个与shell典型的互动:
[root@localhost epoll]# ls
client.cpp readme.md server.cpp utility.h
[root@localhost epoll]# ps
PID TTY TIME CMD
3451 pts/0 00:00:00 bash
3514 pts/0 00:00:00 ps
用下面的时间轴来表示事件的发生次序,从左向右,shell从用户读入字符串"ls",shell建立一个新的进程,然后在那个进程中运行ls程序并等待那个进程结束.

然后shell读取新的一行输入,建立一个新的进程,在这个进程中运行程序并等待这个进程结束,所以就是一下过程的循环:
- 获取命令行
- 解析命令行
- 建立一个子进程(
fork) - 替换子进程(
execvp) - 父进程等待子进程退出
2. 代码实现
#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#define NUM 1024
#define SIZE 128
#define SEP " "
char command_line[NUM];
char* command_args[SIZE];
char env_buffer[NUM];
int ChangeDir(const char* new_path)
{
chdir(new_path);
return 0; //调用成功
}
void PutEnvInMyShell(char* new_env)
{
putenv(new_env);
}
int main()
{
//shell 本质就是一个死循环
while(1)
{
//1. 显示提示符
printf("[smf@我的主机名 当前目录]# ");
fflush(stdout);
//2. 获取用户输入
memset(command_line, '\0', sizeof(command_line));
fgets(command_line, NUM, stdin); //从stdin中读NUM长度的字符串到command_line中
command_line[strlen(command_line) - 1] = '\0'; //清空'\n'
//3. "la -a -l" -> "ls" "-a" "-l"字符串切分
command_args[0] = strtok(command_line, SEP);
int index = 1;
//给ls命令添加颜色
if(strcmp(command_args[0], "ls") == 0)
command_args[index++] = (char*)"--color=auto";
while(command_args[index++] = strtok(NULL, SEP));
//4. 执行、内建命令
if(strcmp(command_args[0], "cd") == 0 && command_args[1] != NULL)
{
ChangeDir(command_args[1]); //让父进程进行路径切换
continue;
}
if(strcmp(command_args[0], "export") == 0 && command_args[1] != NULL)
{
strcpy(env_buffer, command_args[1]);
PutEnvInMyShell(env_buffer);
continue;
}
//5. 创建进程、执行
pid_t id = fork();
if(id == 0)
{
//child 进行程序替换
execvp(command_args[0], command_args);
exit(1);
}
int status = 0;
pid_t ret = waitpid(id, &status, 0);
if(ret > 0)
{
printf("等待子进程成功, sig: %d, code: %d\n", status&0x7F, (status>>8)&0xFF);
}
}
}
本文档详细介绍了如何使用C语言实现一个简单的Shell程序。主要流程包括:显示提示符、获取用户输入、命令解析、执行内建命令(如`cd`、`export`)以及通过`fork`和`execvp`创建及替换子进程执行外部命令。示例代码展示了如何处理用户输入的`ls`命令,并添加颜色选项。
1541

被折叠的 条评论
为什么被折叠?



