原题见《操作系统—精髓与设计原理(第五版)》一书110页。
这里是实现代码。实现了基本的功能,主要通过系统调用,实验环境为Fedora 16 linux 。
//myshell.h
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define MAX_BUFFER 1024 //字符串长度最大值
#define MAX_ARGS 64 //参数最大数量
#define SWEPARATOR " \n\t"
//myshell.c
/**********************************************************
myshell-basic shell replacement
>my shell
从键盘输入一行字符串,分割字符串并解析为cmd命令
clr 清屏
ls <directory> 列出<directory>目录下面的文件,默认为当前目录
environ 环境变量
quit 退出
**********************************************************/
#include<myshell.h>
extern char **environ;
/**********************************************************/
int main(int argc,char** argv)
{
//system("title MyShell");
char linebuf[MAX_BUFFER];
char cmndbuf[MAX_BUFFER];
char *args[MAX_ARGS];
char **arg;
char* prompt="==>";
while(!feof(stdin))
{
system("pwd");
fputs(prompt,stdout);
fflush(stdout);
//输入参数
if(fgets(linebuf,MAX_BUFFER,stdin))
{
arg=args;
*arg++=strtok(linebuf,SWEPARATOR);//按空格,换行符,制表符等分割字符串
while((*arg++=strtok(NULL,SWEPARATOR)));
if(args[0])//第一个参数存在
{
//printf("args[0]:%s\n",args[0]);
cmndbuf[0]=0;
//printf("cmndbuf[0]:%c\n%s\n",cmndbuf[0],args[0]);
//检查是否为系统已经存在的命令
if(!strcmp(args[0],"clr"))
{
strcpy(cmndbuf,"clear");
}
else if(!strcmp(args[0],"dir"))
{
strcpy(cmndbuf,"ls ");
if(!args[1])
{
args[1]=".";//没输入参数默认为当前目录
}
strcat(cmndbuf,args[1]);
//printf("%s",cmndbuf);
}
else if(!strcmp(args[0],"help"))
{
strcpy(cmndbuf,"more ./readme");//显示当前目录下的readme
}
else if(!strcmp(args[0],"pause"))
{
//printf("Press enter to continue...\n");//提示暂停,直到按下回车键
strcpy(cmndbuf,"read -n 1 -p \"Press any key to continue...\"");
// char temp=malloc(sizeof(char));
// scanf("%c",&temp);
// while(strcmp(temp,'\n'))
// {
// scanf("%c",&temp);
// }
}
else if(!strcmp(args[0],"cd"))
{
if(!args[1])
{
args[1]=".";//没输入参数默认为当前目录
}
int result=chdir(args[1]);
if(result<0)
{printf("Error:directory not exist!\n");}
}
else if(!strcmp(args[0],"environ"))
{
char** envstr=environ;
while(*envstr)
{
printf("%s\n",*envstr);
envstr++;
}
}
else if(!strcmp(args[0],"quit"))
{
break;//return 0,退出循环
}
else //其他命令
{
int i=1;
strcpy(cmndbuf,args[0]);
while(args[i])
{
strcat(cmndbuf," ");
strcat(cmndbuf,args[i++]);
}
}
//调用系统命令
if(cmndbuf[0])
{
system(cmndbuf);
}
}
}
}
return 0;
}
其中的cd命令通过chdir函数实现。通过查资料和实验得知,在shell里面直接用cd命令实现工作目录切换不可以,因为system(command)是通过创建子进程实现系统命令调用,原则上是子进程执行cd命令,shell并未改变目录,因此必须通过其他方式比如调用chdir函数实现。
以下是makefile
#makefile
myshell:myshell.c myshell.h
gcc -I. myshell.c -o myshell
以下是readme
//readme
myshell - 一个简单的shell
概要
myshell
版权
myshell is Copyright (C) 2012 by Duan Cong.
介绍
myshell 是一个简单的shell程序,通过它你可以用来实现简单的命令行操作,比如列出目录下面的文件,清屏等等。
用法
在终端下输入myshell,即可打开程序。
后台运行:在终端下输入myshell时,紧接着输入一个&,即输入myshell&。
功能
当进入myshell后,你可以通过输入相应命令实现以下功能:
cd [directory] —— 把当前默认目录改变为[directory]。不输入参数默认为当前目录。
dir [directory] —— 列出目录[directory]的内容。不输入参数默认为当前目录。
clr —— 清屏。
environ —— 列出所有的环境变量。
echo [comment] —— 在屏幕上显示[comment]并换行。
help —— 显示本用户手册。
quit —— 退出myshell。
还调用其他程序和系统命令。
说明
输入输出重定向功能:
在myshell中使用命令行如programname arg1 arg2 > outputfile 时,programname的输出将输出到outputfile里而不是显示屏幕上。如果outputfile已存在则覆盖已有文件,如果不存在则创建输出文件。
在myshell中使用命令行如programname arg1 arg2 >> outputfile 时,programname的输出将追加到outputfile里而不是显示屏幕上。如果outputfile已存在则将输出添加到已有文件尾部,如果不存在则创建输出文件。
示例
当前目录为/home/dc,在终端下输入myshell,显示如下:
[dc@localhost ~]$ myshell
/home/dc
==>
说明已经进入程序。
继续输入dir,显示如下:
/home/dc
==>dir
A Desktop index.php note.xml Public Untitled 1.docx
Audiobooks Documents index.php.bak Pictures Templates Videos
blog.docx Downloads Music Podcasts test.txt
/home/dc
==>
输入clr,显示如下:
/home/dc
==>
屏幕被清空。
输入environ,显示如下(中间省略部分环境变量,用省略号代替):
/home/dc
==>environ
XDG_VTNR=1
XDG_SESSION_ID=2
HOSTNAME=localhost.localdomain
IMSETTINGS_INTEGRATE_DESKTOP=yes
GPG_AGENT_INFO=/tmp/keyring-sFt4mm/gpg:0:1
TERM=xterm
SHELL=/bin/bash
...
XAUTHORITY=/var/run/gdm/auth-for-dc-mesabL/database
_=/home/dc/Desktop/operating_system/project/myshell
OLDPWD=/home/dc/Desktop/operating_system/project
/home/dc
==>
输入dir /,显示如下:
/home/dc
==>dir /
bin dev home lost+found mnt proc run srv tmp var
boot etc lib media opt root sbin sys usr
/home/dc
==>
另外,myshell可以调用其他程序,例如输入gedit,将调用GUI的文本编辑器gedit,在终端中按下ctrl+C可以结束gedit进程。
在myshell中输入quit,退出myshell,返回终端,显示如下:
/home/dc
==>quit
[dc@localhost ~]$
以上仅为部分示例,更多详细请参考“功能”。