1.题目要求
- 该程序读取用户输入的命令,调用函数mysys(上一个作业)执行用户的命令,示例如下
# 编译sh1.c
$ cc -o sh1 sh1.c
# 执行sh1
$ ./sh
# sh1打印提示符>,同时读取用户输入的命令echo,并执行输出结果
> echo a b c
a b c
# sh1打印提示符>,同时读取用户输入的命令cat,并执行输出结果
> cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
- 请考虑如何实现内置命令cd、pwd、exit
2.解决思路
考虑实现基本功能,只需要在mysys.c
的基础上修改main
函数中的内容,原本是直接指定了mysys
的参数,本程序需要读取用户输入的命令command
,并将command
传递到mysys
函数中。这里读取屏幕上的内容和输出“>”
到屏幕上采用文件读写的方式,标准输入、标准输出的文件描述符分别为0、1。
int main() {
char command[SIZE];
int count;
while(1) {
write(1, ">", sizeof(">"));
count = read(0, command, sizeof(command));
command[count] = 0;
mysys(command);
}
return 0;
}
再考虑如何实现内置命令cd
、pwd
、exit
。内置命令的实现无需创建新进程,因此在mysys
函数中调用parse_command
分割字符串之后,先进行判断是否为内置命令,若是则调用子函数handle_built_in_command
单独处理并返回,若不是则执行mysys
中已实现的通过execvp
系统调用装入程序并执行,再使用wait
系统调用等待子进程结束。
子函数handle_built_in_command
中分别处理内置命令cd
、pwd
、exit
,实现cd
需要调用库函数chdir
改变当前的工作目录。chdir
函数的相关说明如下:
头文件:#include <unistd.h>
定义函数:int chdir(const char * path);
函数说明:chdir()用来将当前的工作目录改变成以参数 path 所指的目录
返回值:执行成功则返回0, 失败返回-1, errno 为错误3.代码
因此将argv[1]
作为参数传递,使当前的工作目录改变成以参数 argv[1]
所指的目录,因为有可能存在该目录不存在的情况,故对返回值作判断,若成功则打印当前工作目录,若失败则打印报错信息。
实现pwd
需要调用库函数getcwd
,getcwd
函数的相关说明如下:
头文件:#include <unistd.h>
定义函数:char * getcwd(char * buf, size_t size);
函数说明:getcwd() 会将当前的工作目录绝对路径复制到参数 buf 所指的内存空间,参数 size 为 buf 的空间大小
返回值:执行成功则将结果复制到参数 buf 所指的内存空间, 或是返回自动配置的字符串指针. 失败返回 NULL,错误3.代码存于errno
需要注意的是在调用此函数时,buf
所指的内存空间要足够大。若工作目录绝对路径的字符串长度超过参数size
大小,则返回NULL
,errno
的值则为ERANGE
。但为buf
开辟的内存空间过大则造成浪费,可以将参数buf
设为NULL
,getcwd()
会依参数size
的大小自动配置内存(使用malloc()
),如果参数 size
也为0,则getcwd()
会依工作目录绝对路径的字符串程度来决定所配置的内存大小,进程可以在使用完此字符串后利用free()
来释放此空间。在具体实现时采用将参数buf
设置为NULL
,将参数size
设置为0的做法,最后使用free释放空间。
实现exit
只需调用exit(0)
终止程序即可。
3.代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#define SIZE 256
#define MAX_ARGC 16
struct Command {
int argc;
char *argv[MAX_ARGC];
} commands;
// 将 command 以空格作为分割符分割
void parse_command(char *command) {
// char line[SIZE]; // 注意不能这样写
char *line = (char *)malloc(sizeof(command));
strcpy(line, command);
commands.argc = 0;
for(int i = 0; i < MAX_ARGC; i++)
commands.argv[i] = NULL;
char *word;
char *delim = " \n";
word = strtok(line, delim);
while(word != NULL) {
commands.argv[commands.argc++] = word;
word = strtok(NULL, delim);
}
}
// 处理内置命令cd、pwd、exit
void handle_built_in_command() {
if(strcmp(commands.argv[0], "cd") == 0) {
int error = chdir(commands.argv[1]);
if(error < 0)
perror("cd");
else {
char *path = getcwd(NULL, 0);
printf("current working directory: %s\n", path);
free(path);
}
}
else if(strcmp(commands.argv[0], "pwd") == 0) {
char *path = getcwd(NULL, 0);
printf("current working directory: %s\n", path);
free(path);
}
else if(strcmp(commands.argv[0], "exit") == 0) {
exit(0);
}
}
void mysys(char *command) {
parse_command(command);
// 单独处理内置命令cd、pwd、exit
if(strcmp(commands.argv[0], "cd") == 0 || strcmp(commands.argv[0], "pwd") == 0 || strcmp(commands.argv[0], "exit") == 0) {
handle_built_in_command();
return ;
}
pid_t pid;
pid = fork();
if(pid == 0) {
int error = execvp(commands.argv[0], commands.argv);
if(error < 0)
perror("execvp");
}
wait(NULL);
}
int main() {
char command[SIZE];
int count;
while(1) {
write(1, ">", sizeof(">"));
count = read(0, command, sizeof(command));
command[count] = 0;
mysys(command);
}
return 0;
}
4.运行结果
$ gcc sh1.c -o sh1
$ ./sh1
>echo a b c
>cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
...
>pwd
current working directory: /home/fanyihua/OS
>cd test
current working directory: /home/fanyihua/OS/test
>cd ..
current working directory: /home/fanyihua/OS
>exit