1.3 实践与案例分析
1.3.1 案例分析:实现一个简单的Shell
本节将通过一个简单的Shell程序来展示如何使用C语言中的高级操作系统功能,包括命令行解析、进程管理(fork
和exec
)、管道和重定向。
1.3.1.1 解析命令行输入
在实现Shell时,第一步是解析用户输入的命令行。这一过程包括读取输入、分割命令和参数。
示例代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_INPUT_SIZE 1024
#define MAX_ARG_SIZE 100
// 函数 parse_input:解析用户输入
void parse_input(char *input, char **args) {
char *token;
token = strtok(input, " \n"); // 使用空格和换行符作为分隔符 [1]
int i = 0;
while (token != NULL) {
args[i++] = token; // 将分割的片段存入 args 数组 [2]
token = strtok(NULL, " \n"); // 获取下一个分割片段 [3]
}
args[i] = NULL; // 参数列表以 NULL 结尾 [4]
}
int main() {
char input[MAX_INPUT_SIZE];
char *args[MAX_ARG_SIZE];
while (1) {
printf("my_shell> ");
if (fgets(input, MAX_INPUT_SIZE, stdin) == NULL) {
// 从标准输入读取一行 [5]
perror("fgets error"); // 错误处理 [6]
exit(1);
}
parse_input(input, args); // 解析输入 [7]
for (int i = 0; args[i] != NULL; i++) {
// 输出解析完成后的参数 [8]
printf("Argument %d: %s\n", i, args[i]);
}
}
return 0;
}
- [1] 使用空格和换行符作为分隔符:
strtok
函数用于将input
中的字符串分割成若干个部分,依据空格和换行符进行分割。 - [2] 将分割的片段存入
args
数组:分割后的每个子字符串被存放在args
数组中,args
作为指针数组,每个元素指向一个字符串片段。 - [3] 获取下一个分割片段:通过在
strtok
函数中传入NULL
,继续获取下一个分割的字符串片段,直到没有更多的分割片段可获取。 - [4] 参数列表以 NULL 结尾:为了便于后续遍历,通过在
args
数组的末尾加上NULL
来标记参数列表的结束。 - [5] 从标准输入读取一行:
fgets
用于从标准输入流(stdin
)中读取一行输入,并存储到input
数组中。 - [6] 错误处理:若
fgets
返回NULL
,则表示出现错误,使用perror
打印错误信息并退出程序。 - [7] 解析输入:调用
parse_input
函数,将用户输入解析成命令和参数。 - [8] 输出解析完成后的参数:循环遍历
args
数组,输出每个解析出来的命令或参数。
1.3.1.2 使用fork
和exec
执行命令
在Shell中,用户命令通过创建子进程并使用exec
族函数执行。
示例代码
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
// 执行命令函数
void