You are on page 1of 6

Unix 实验报告

用 c 语言编写一个简单的 shell。这个小程序可以执行不带参数的外部命令,支持标准 I/O 重定向,还可


以通过管道连接两个命令。

(一)实现原理
1、不带参数的命令
将命令字符串提取出来之后,直接调用 execlp()函数执行。
2、重定向输入
将指定的文件打开后,使用 dup2()函数将文件描述符与 stdin 的文件描述符关联起来,从而将命令的
标准输入转移到指定的文件。
3、重定向输出
(1)覆盖性重定向输出
指带有“>>”形式的命令。以只写方式打开(如果文件不存在则创建)文件,使用 dup2()函数将文件
描述符与 stdout 关联起来,从而将命令的标准输出转移到指定文件。
(2)追加性重定向输出
相对与覆盖性重定向输出,区别只在于打开文件时制定 O——APPEND 参数,从而在写入新内容时从
末尾添加新数据。
4、管道
使用 dup2()函数,将父进程的 stdin 与子进程的 stdout 关联起来,从而将子进程的输出直接作为父进
程的标准输入,达到管道的目的。但要注意保证父进程要等到子进程执行完后再执行。

(二)流程图(伪代码)
while(1) {
显示命令提示符;
将命令以字符串形式读入 buf[1000];
解析字符串
{
以空格为分界线将命令划分为三段;
分别存入 argv[0], argv[1], argv[2];
}

if(argv[1] == “exit” )
退出程序;

执行命令
{
fork()创建子进程;
switch( argv[1][0]) {
case '\0': //说明为不带参数的命令
if(pid == 0) //进入子进程
调用 execlp()函数执行程序;
while(wait(&status) != pid2) ; //保证子进程完成后再运行父进程
break; //结束本次执行,进入下一个指令流程
case ‘|': //说明为管道类型的命令
调用 fork()创建孙进程
调用 pipe()创建管道文件
if(pid == 0) { //进入子进程
调用 execlp()函数执行程序;
将孙进程 stdout 关联到子进程 stdin
调用 execlp()函数运行管道前命令;
}
while(wait(&status) != pid2) ; //保证孙进程完成后再运行子进程
if(pid == 0) //进入子进程
调用 execlp()函数执行程序;
运行子进程
while(wait(&status) != pid2) ; //保证子进程完成后再运行父进程
break; //结束本次执行,进入下一个指令流程
case ‘<': //说明为重定向输入类型的命令
fd1 = open(argv[2],O_RDONLY); //只读方式打开指定文件
dup2()将 fd1 与 stdin 关联起来
if(pid == 0) //进入子进程
调用 execlp()函数执行程序;
while(wait(&status) != pid2) ; //保证子进程完成后再运行父进程
break; //结束本次执行,进入下一个指令流程
case '>':
if( argv[1][1] == '>') { //说明为覆盖性重定向输出
fd1 = open(argv[2],O_RDONLY); //只写方式打开指定文件
dup2()将 fd1 与 stdout 关联起来
if(pid == 0) //进入子进程
调用 execlp()函数执行程序;
while(wait(&status) != pid2) ; //保证子进程完成后再运行父进程
break; //结束本次执行,进入下一个指令流程
} else { //说明为追加性重定向输出
fd1 = open(argv[2],O_RDONLY); //追加方式打开指定文件
dup2()将 fd1 与 stdout 关联起来
if(pid == 0) //进入子进程
调用 execlp()函数执行程序;
while(wait(&status) != pid2) ; //保证子进程完成后再运行父进程
break; //结束本次执行,进入下一个指令流程
}
}
}

(三)运行结果(截图)
运行结果解析:
(1)命令提示符为”>”
(2)运行命令 ls,结果正确显示。测试不带参数的命令的执行。
(3)运行 ls | wc,结果正确显示。测试管道命令的执行。
(4)运行 ls > testfile
(5)运行 cat < testfile,综合上一步结果显示正确。测试输入输出重定向。
(6)运行 ls > testfile
(7)运行 cat < testfile,综合上一步结果显示正确。测试追加性输出重定向。
(8)运行 ls >> testfile
(9)运行 cat < testfile,综合上一步结果显示正确。测试覆盖性输出重定向。
(10)运行 exit,成功退出程序。

(四)源代码
#include "stdio.h"
#include "stdlib.h"
#include "unistd.h"
#include "string.h"
#include "sys/wait.h"
#include "fcntl.h"
int main() {
char argv[3][100], buf[1000];
int i, j, k;
int pid, pid2;
int fd[2], fd1;
int status;

while(1) {
//获取命令并进行解析
for(i=0;i<3;i++)
for(j=0;j<100;j++)
argv[i][j] = '\0';
for(k=0;k<1000;k++)
buf[k] = '\0';
i = 0;
j = 0;

printf(">");
fflush(stdout);
read(0, buf, 1000);
while(buf[i] != '\0' && buf[i] != '\n') {
if(buf[i] == ' ')
i++;
else {
k = 0;
while(buf[i] != ' ' && buf[i] != '\n')
argv[j][k++] = buf[i++];
j++;
}
}
//结束成熟
if(strcmp(argv[0], "exit") == 0)
return 0;
//执行命令
switch(argv[1][0]) {
case '\0':
if((pid = fork()) < 0) {
perror("fork error");
} else if(pid == 0) {
if(execlp(argv[0], argv[0], (char *)0)<0)
exit(0);
}
while(wait(&status) != pid) ;
break;
case '|':
if(argv[1][1] == '\0') {
if((pid = fork()) < 0) {
perror("fork error");
} else if(pid == 0) {
pipe(fd);
if((pid2 = fork()) < 0) {
perror("fork error");
} else if(pid2 == 0) {
dup2(fd[1],1);
if(execlp(argv[0], argv[0], (char *)0)<0)
exit(0);
} else {
while(wait(&status) != pid2) ;
close(fd[1]);
dup2(fd[0],0);
if(execlp(argv[2], argv[2], (char *)0)<0)
exit(0);
close(fd[0]);
}
}
while(wait(&status) != pid) ;
break;
}
case '<':
if(argv[1][1] == '\0') {
if((pid = fork()) < 0) {
perror("fork error");
} else if(pid == 0) {
fd1 = open(argv[2],O_RDONLY);
dup2(fd1, 0);
close(fd1);
if(execlp(argv[0], argv[0], (char *)0)<0)
exit(0);
}
while(wait(&status) != pid) ;
break;
}
case '>':
if(argv[1][1] == '>' && argv[1][2] == '\0') {
if((pid = fork()) < 0) {
perror("fork error");
} else if(pid == 0) {
fd1 = open(argv[2], O_CREAT|O_TRUNC|O_WRONLY);
dup2(fd1,1);
if(execlp(argv[0], argv[0], (char *)0)<0)
exit(0);
close(fd1);
}
while(wait(&status) != pid) ;
break;
} else if(argv[1][1] == '\0') {
if((pid = fork()) < 0) {
perror("fork error");
} else if(pid == 0) {
fd1 = open(argv[2], O_CREAT|O_APPEND|O_WRONLY);
dup2(fd1,1);
if(execlp(argv[0], argv[0], (char *)0)<0)
exit(0);
close(fd1);
}
while(wait(&status) != pid) ;
break;
}
default:
break;
}
}
}

You might also like