Linux 命令解释程序设计与实现

1.实验目的

探索、理解并掌握操作系统命令解释器的设计原理和实现机制,基于 Linux 内核进行相

应命令解释程序的设计和实现,并在 Linux 操作系统平台上加以测试验证。

2.实验内容 

分析、设计与实现基于Linux 内核的命令解释程序(Shell),主要包括系统环境变量的设置和初始化、系统命令提示符显示、命令辨别解析(区分内部命令与外部命令及不同内部命令)、典型内部命令(譬如显示指定目录下文件列表、显示文本文件内容、文件拷贝、文件删除、空文件创建、日期设置/显示)处理等功能,并在 Linux 操作系统上测试验证。

3.实验要求 

Linux 命令解释程序功能设计要求:

1)选取和设计实现一组内部命令(五条以上); 

2)外部命令执行采用直接调用 exec 系统调用的方式来实现;

3)至少一条内部命令采用直接调用相应系统调用的方式来实现;

4)系统环境变量(至少包括用户主目录 HOME 和可执行程序搜索路径目录 PATH)支持;

5)在 Linux操作系统上启用(或替换原命令解释程序 Shell)并测试验证。

4.实验环境

开发环境

Ubuntu、Visual Studio Code

运行环境

GCC、linux shell

测试环境

终端(笔记本)

5.实验设计与实现

5.1实验总体设计

5.1.1命令设计

本次实验设计了一组有8条内部命令并能执行外部命令的的命令解释程序,其中5条内部命令为系统直接调用。

其中5条系统直接调用的内部命令为:

命令

功能

my_cd

切换目录

my_pwd

打印当前目录

my_mkdir

创建目录

my_rmdir

删除目录

my_env

显示系统环境变量

其余3条内部命令为:

命令

功能

my_echo

打印用户输入

my_cat

读取并打印文件

my_write

写入或改写文件内容

5.1.2程序总体设计

执行外部命令的程序设计如下:

  程序总体设计如下:

5.1.3函数设计
  1. main()

功能:程序的主入口,处理用户输入和命令执行。

说明:此函数使用循环来不断接收用户输入并调用 execute_command 函数处理命令。当用户输入 "exit" 时,程序退出。

int main(){  
    print_menu();  
     
    char input[256];  
     
    while(1){  
        // 获取用户输入  
        fgets(input, 256, stdin);  
        input[strcspn(input, "\n")] = 0;  // 去除换行符  
          
        // 输入exit,退出  
        if(strcmp(input, "exit") == 0){  
            break;  
        }  
          
        execute_command(input);  // 解析并执行命令  
        print_line();  
    }  
    return 0;  
}  

  1. print_menu()

功能:打印程序支持的命令列表。

说明:此函数不涉及系统调用,它仅使用 printf 函数来显示菜单。

// 打印菜单  
void print_menu(){  
    printf("-------------------my_shell------------------\n");  
    printf("输入“my_cd”命令:切换目录\n");  
    printf("输入“my_pwd”命令:打印当前目录\n");  
    printf("输入“my_echo”命令:打印用户输入\n");  
    printf("输入“my_mkdir”命令:创建目录\n");  
    printf("输入“my_rmdir”命令:删除目录\n");  
    printf("输入“my_cat”命令:打开并读取文件内容\n");  
    printf("输入“my_write”命令:写入文件内容\n");  
    printf("输入“my_env”命令:显示系统环境变量\n");  
    printf("---------------------------------------------\n");  
}  

  1. handle_cd(char *args[])

功能:处理目录切换命令。

说明:

系统调用:chdir() 用于改变当前工作目录。

参数:

args[]:命令行参数数组。args[1] 应包含要切换到的目录路径。

1.// my_cd内部命令,切换目录  
2.void handle_cd(char *args[]){  
3.    if(args[1] == NULL){  
4.        //  
5.        chdir(getenv("HOME"));  // 切换到 HOME 目录  
6.    } else if(chdir(args[1]) == 0){  
7.        printf("目录切换成功,当前在 %s\n", args[1]);  
8.    } else {  
9.        perror("你输入的路径错误!\n");  
10.    }    
11.}  
  1.  handle_pwd()

功能:打印当前工作目录。

说明:

系统调用:getcwd() 用于获取当前工作目录的路径。

参数:

cwd:字符数组,用于存储当前工作目录的路径。

sizeof(cwd):cwd 数组的大小。

1.// my_pwd命令:打印当前目录  
2.void handle_pwd(){  
3.    char cwd[1024];  
4.    if(getcwd(cwd, sizeof(cwd)) != NULL){  
5.        printf("%s\n", cwd);  
6.    } else {  
7.        perror("获取目录失败!");  
8.    }  
} 
  1. handle_echo(char *args[])

功能:回显用户输入的命令。

说明:此函数不涉及系统调用,使用循环和 printf 函数输出所有参数。

1.// my_echo命令:打印用户输入(可打印空格)  
2.void handle_echo(char *args[]){  
3.    for(int i = 1; args[i] != NULL; i++){  
4.        printf("%s ", args[i]);  
5.    }  
6.    printf("\n");  
7.}  
  1. handle_mkdir(char *args[])

功能:创建新目录。

说明:

系统调用:mkdir() 用于创建一个新目录。

参数:

args[1]:要创建的目录名。

0777:设置新目录的权限。

1.// my_mkdir命令:创建目录  
2.void handle_mkdir(char *args[]){  
3.    if(args[1] == NULL){  
4.        fprintf(stderr, "my_mkdir: missing operand\n");  
5.    } else if(mkdir(args[1], 0777) == 0){  
6.        printf("创建成功!\n");  
7.    } else {  
8.        perror("创建失败!");  
9.    }  
10.}  
  1. handle_rmdir(char *args[])

功能:删除空目录。

说明:

系统调用:rmdir() 用于删除一个空目录。

参数:

args[1]:要删除的目录名。

1.// my_mkdir命令:创建目录  
2.void handle_mkdir(char *args[]){  
3.    if(args[1] == NULL){  
4.        fprintf(stderr, "my_mkdir: missing operand\n");  
5.    } else if(mkdir(args[1], 0777) == 0){  
6.        printf("创建成功!\n");  
7.    } else {  
8.        perror("创建失败!");  
9.    }  
10.}  
  1. handle_cat(char *args[])

功能:读取并显示文件内容。

说明:

系统调用:fopen() 和 fgets() 用于打开文件并逐行读取内容。

参数:

args[1]:要读取的文件名。

line:用于存储从文件中读取的每一行数据。

1.// my_cat 命令:读取并打印文件内容  
2.void handle_cat(char *args[]) {  
3.    if (args[1] == NULL) {  
4.        fprintf(stderr, "my_cat: missing operand\n");  
5.        return;  
6.    }  
7.      
8.    FILE *file = fopen(args[1], "r");  
9.    if (file == NULL) {  
10.        perror("无法打开文件");  
11.        return;  
12.    }  
13.      
14.    char line[256];  
15.    while (fgets(line, sizeof(line), file) != NULL) {  
16.        printf("%s", line);  
17.    }  
18.      
19.    fclose(file);  
20.}  
  1. handle_write(char *args[])

功能:写入或改写文件内容。

说明:

参数:

args[1]:文件名。

O_WRONLY | O_CREAT | O_TRUNC:打开文件的标志,表示写入、创建和截断。

0644:文件权限。

1.// my_write 命令:写入或改写文件内容  
2.void handle_write(char *args[]) {  
3.    if (args[1] == NULL) {  
4.        fprintf(stderr, "my_write: missing operand (文件名)\n");  
5.        return;  
6.    }  
7.      
8.    int fd = open(args[1], O_WRONLY | O_CREAT | O_TRUNC, 0644);  
9.    if (fd < 0) {  
10.        perror("无法打开文件");  
11.        return;  
12.    }  
13.      
14.    printf("输入要写入的内容(输入 'done' 结束):\n");  
15.    char buffer[256];  
16.    while (1) {  
17.        fgets(buffer, sizeof(buffer), stdin);  
18.        buffer[strcspn(buffer, "\n")] = 0;  // 去除换行符  
19.          
20.        if (strcmp(buffer, "done") == 0) {  
21.            break;  
22.        }  
23.          
24.        write(fd, buffer, strlen(buffer));  
25.        write(fd, "\n", 1);  // 添加换行符  
26.    }  
27.      
28.    close(fd);  
29.}  
  1. handle_env()    (进行system调用)

功能:显示系统环境变量 HOME 和 PATH。

说明:

此函数使用 system() 系统调用来执行 shell 命令,从而获取并打印环境变量 HOME 和 PATH 的值。

system() 是一个 C 标准库函数,它允许程序执行外部命令。

系统调用:system()

功能:执行指定的命令。

1.// my_env 命令:显示系统环境变量  
2.// 使用 system() 函数获取并打印 HOME 和 PATH 环境变量  
3.void handle_env() {  
4.    // 调用 system() 执行 shell 命令来打印 HOME 环境变量  
5.    printf("HOME: ");  
6.    system("echo $HOME");  
7.  
8.    // 调用 system() 执行 shell 命令来打印 PATH 环境变量  
9.    printf("PATH: ");  
10.    system("echo $PATH");  
11.}  
  1. execute_command

功能:解析并执行命令。

说明:

系统调用:fork(), execvp(), wait() 用于创建子进程、执行外部命令和等待子进程结束。

参数:

input:用户输入的命令字符串。

strtok():用于分割命令字符串。

args[]:存储分割后的命令参数。

1.// 解析并执行内部命令  
2.void execute_command(char *input){  
3.    char *args[64];  
4.      
5.    // 使用strtok函数进行字符串分割  
6.    char *token = strtok(input, " ");  
7.    int i = 0;  
8.  
9.    while(token != NULL){  
10.        args[i++] = token;  
11.        token = strtok(NULL, " ");  
12.    }  
13.    args[i] = NULL;  
14.  
15.    // 处理内部命令  
16.    if (strcmp(args[0], "my_cd") == 0) {  
17.        handle_cd(args);  
18.    } else if (strcmp(args[0], "my_pwd") == 0) {  
19.        handle_pwd();  
20.    } else if (strcmp(args[0], "my_echo") == 0) {  
21.        handle_echo(args);  
22.    } else if (strcmp(args[0], "my_mkdir") == 0) {  
23.        handle_mkdir(args);  
24.    } else if (strcmp(args[0], "my_rmdir") == 0) {  
25.        handle_rmdir(args);  
26.    } else if (strcmp(args[0], "my_cat") == 0) {  
27.        handle_cat(args);  
28.    } else if (strcmp(args[0], "my_write") == 0) {  
29.        handle_write(args);  
30.    } else if (strcmp(args[0], "my_env") == 0) {  
31.        handle_env();   // 显示环境变量  
32.    } else {  
33.        // 外部命令处理  
34.        printf("此条命令为外部命令\n");  
35.          
36.        pid_t pid = fork();  
37.          
38.        if(pid == 0){  
39.            execvp(args[0], args); // 使用PATH查找并执行外部命令  
40.            perror("execvp failed");  
41.            exit(EXIT_FAILURE);  
42.        } else if(pid > 0){  
43.            wait(NULL);   // 等待子进程结束  
44.        } else {  
45.            perror("调用子进程失败!\n");  
46.        }  
47.    }  
48.}  

完整代码如下:

21.#include <stdio.h>  
22.#include <string.h>  
23.#include <unistd.h>  
24.#include <stdlib.h>  
25.#include <sys/wait.h>  
26.#include <sys/stat.h>  
27.#include <fcntl.h>  
28.  
29.// 打印菜单  
30.void print_menu(){  
31.    printf("-------------------my_shell------------------\n");  
32.    printf("输入“my_cd”命令:切换目录\n");  
33.    printf("输入“my_pwd”命令:打印当前目录\n");  
34.    printf("输入“my_echo”命令:打印用户输入\n");  
35.    printf("输入“my_mkdir”命令:创建目录\n");  
36.    printf("输入“my_rmdir”命令:删除目录\n");  
37.    printf("输入“my_cat”命令:打开并读取文件内容\n");  
38.    printf("输入“my_write”命令:写入文件内容\n");  
39.    printf("输入“my_env”命令:显示系统环境变量\n");  
40.    printf("---------------------------------------------\n");  
41.}  
42.  
43.// 打印分割线  
44.void print_line(){  
45.    printf("---------------------------------------------\n");  
46.}  
47.  
48.// my_cd内部命令,切换目录  
49.void handle_cd(char *args[]){  
50.    if(args[1] == NULL){  
51.        //  
52.        chdir(getenv("HOME"));  // 切换到 HOME 目录  
53.    } else if(chdir(args[1]) == 0){  
54.        printf("目录切换成功,当前在 %s\n", args[1]);  
55.    } else {  
56.        perror("你输入的路径错误!\n");  
57.    }    
58.}  
59.  
60.// my_pwd命令:打印当前目录  
61.void handle_pwd(){  
62.    char cwd[1024];  
63.    if(getcwd(cwd, sizeof(cwd)) != NULL){  
64.        printf("%s\n", cwd);  
65.    } else {  
66.        perror("获取目录失败!");  
67.    }  
68.}  
69.  
70.// my_echo命令:打印用户输入(可打印空格)  
71.void handle_echo(char *args[]){  
72.    for(int i = 1; args[i] != NULL; i++){  
73.        printf("%s ", args[i]);  
74.    }  
75.    printf("\n");  
76.}  
77.  
78.// my_mkdir命令:创建目录  
79.void handle_mkdir(char *args[]){  
80.    if(args[1] == NULL){  
81.        fprintf(stderr, "my_mkdir: missing operand\n");  
82.    } else if(mkdir(args[1], 0777) == 0){  
83.        printf("创建成功!\n");  
84.    } else {  
85.        perror("创建失败!");  
86.    }  
87.}  
88.  
89.// my_rmdir 命令:删除目录  
90.void handle_rmdir(char *args[]) {  
91.    if(args[1] == NULL) {  
92.        fprintf(stderr, "缺少操作数!\n");  
93.    } else if(rmdir(args[1]) == 0) {  
94.        printf("删除成功!\n");  
95.    } else {  
96.        perror("删除失败");  
97.    }  
98.}  
99.  
100.// my_cat 命令:读取并打印文件内容  
101.void handle_cat(char *args[]) {  
102.    if (args[1] == NULL) {  
103.        fprintf(stderr, "my_cat: missing operand\n");  
104.        return;  
105.    }  
106.      
107.    FILE *file = fopen(args[1], "r");  
108.    if (file == NULL) {  
109.        perror("无法打开文件");  
110.        return;  
111.    }  
112.      
113.    char line[256];  
114.    while (fgets(line, sizeof(line), file) != NULL) {  
115.        printf("%s", line);  
116.    }  
117.      
118.    fclose(file);  
119.}  
120.  
121.// my_write 命令:写入或改写文件内容  
122.void handle_write(char *args[]) {  
123.    if (args[1] == NULL) {  
124.        fprintf(stderr, "my_write: missing operand (文件名)\n");  
125.        return;  
126.    }  
127.      
128.    int fd = open(args[1], O_WRONLY | O_CREAT | O_TRUNC, 0644);  
129.    if (fd < 0) {  
130.        perror("无法打开文件");  
131.        return;  
132.    }  
133.      
134.    printf("输入要写入的内容(输入 'done' 结束):\n");  
135.    char buffer[256];  
136.    while (1) {  
137.        fgets(buffer, sizeof(buffer), stdin);  
138.        buffer[strcspn(buffer, "\n")] = 0;  // 去除换行符  
139.          
140.        if (strcmp(buffer, "done") == 0) {  
141.            break;  
142.        }  
143.          
144.        write(fd, buffer, strlen(buffer));  
145.        write(fd, "\n", 1);  // 添加换行符  
146.    }  
147.      
148.    close(fd);  
149.}  
150.  
151.// my_env 命令:显示系统环境变量  
152.// 使用 system() 函数获取并打印 HOME 和 PATH 环境变量  
153.void handle_env() {  
154.    // 调用 system() 执行 shell 命令来打印 HOME 环境变量  
155.    printf("HOME: ");  
156.    system("echo $HOME");  
157.  
158.    // 调用 system() 执行 shell 命令来打印 PATH 环境变量  
159.    printf("PATH: ");  
160.    system("echo $PATH");  
161.}  
162.// 解析并执行内部命令  
163.void execute_command(char *input){  
164.    char *args[64];  
165.      
166.    // 使用strtok函数进行字符串分割  
167.    char *token = strtok(input, " ");  
168.    int i = 0;  
169.  
170.    while(token != NULL){  
171.        args[i++] = token;  
172.        token = strtok(NULL, " ");  
173.    }  
174.    args[i] = NULL;  
175.  
176.    // 处理内部命令  
177.    if (strcmp(args[0], "my_cd") == 0) {  
178.        handle_cd(args);  
179.    } else if (strcmp(args[0], "my_pwd") == 0) {  
180.        handle_pwd();  
181.    } else if (strcmp(args[0], "my_echo") == 0) {  
182.        handle_echo(args);  
183.    } else if (strcmp(args[0], "my_mkdir") == 0) {  
184.        handle_mkdir(args);  
185.    } else if (strcmp(args[0], "my_rmdir") == 0) {  
186.        handle_rmdir(args);  
187.    } else if (strcmp(args[0], "my_cat") == 0) {  
188.        handle_cat(args);  
189.    } else if (strcmp(args[0], "my_write") == 0) {  
190.        handle_write(args);  
191.    } else if (strcmp(args[0], "my_env") == 0) {  
192.        handle_env();   // 显示环境变量  
193.    } else {  
194.        // 外部命令处理  
195.        printf("此条命令为外部命令\n");  
196.          
197.        pid_t pid = fork();  
198.          
199.        if(pid == 0){  
200.            execvp(args[0], args); // 使用PATH查找并执行外部命令  
201.            perror("execvp failed");  
202.            exit(EXIT_FAILURE);  
203.        } else if(pid > 0){  
204.            wait(NULL);   // 等待子进程结束  
205.        } else {  
206.            perror("调用子进程失败!\n");  
207.        }  
208.    }  
209.}  
210.  
211.int main(){  
212.    print_menu();  
213.     
214.    char input[256];  
215.     
216.    while(1){  
217.        // 获取用户输入  
218.        fgets(input, 256, stdin);  
219.        input[strcspn(input, "\n")] = 0;  // 去除换行符  
220.          
221.        // 输入exit,退出  
222.        if(strcmp(input, "exit") == 0){  
223.            break;  
224.        }  
225.          
226.        execute_command(input);  // 解析并执行命令  
227.        print_line();  
228.    }  
229.    return 0;  
230.}  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值