Write a Shell in C 学习(二)

上一次简单的谈了怎样从命令行读取输入并对输入进行初步的处理。下一步就是所谓执行命令了。而命令大概又分成两个部分,一是内建命令,二是执行外部程序。这篇主要讲如喝执行内建命令。

我实现的内建命令有,cd,info,pwd,exit,search。值得一提的是,我把执行外部程序的命令ex也放到了内建命令中,只是为了方便管理。具体代码如下。

#define CWD_LENTH 64

/*
  Function Declarations for builtin shell commands:
 */
int ssh_cd(char **args);
int ssh_info(char **args);
int ssh_exit(char **args);
int ssh_pwd(char **args);
int ssh_search(char **args);

/*
  List of builtin commands, followed by their corresponding functions.
 */
char *builtin_str[] = {
  "cd",
  "info",
  "exit",
  "pwd",
  "search",
  "ex"
};

/*
  An array of function pointers.
*/
int (*builtin_func[]) (char **) = {
	&ssh_cd,
	&ssh_info,
	&ssh_exit,
	&ssh_pwd,
	&ssh_search,
	&ssh_launch
};

int ssh_num_builtins() {
	return sizeof(builtin_str) / sizeof(char *);
}

/*
  Builtin function implementations.
*/
int ssh_cd(char **args)
{
  if (args[1] == NULL) {
    fprintf(stderr, "ssh: expected argument to \"cd\"\n");
  } else {
    if (chdir(args[1]) != 0) {
      perror("ssh");
    }
  }
  return 1;
}

int ssh_info(char **args)
{
  printf("XJCO2211 Simplified Shell by sc16h3s.\n");

  return 1;
}

int ssh_exit(char **args)
{
  return 0;
}

int ssh_pwd(char **args)
{
	char cwd[CWD_LENTH];

	getcwd(cwd,CWD_LENTH);
	printf("Current working directory is %s.\n",cwd);

	return 1;
}

int ssh_search(char **args)
{
	if (args[1] == NULL){
		printf("Please input values after 'search'.\n");
		return 1;
	}

	char cwd[CWD_LENTH];
	char **filenames = (char **)malloc(sizeof(char *) * 1024);
	char **filegets = (char **)malloc(sizeof(char *) * 1024);
	int number = 0;
	DIR *dirptr = NULL;
	struct dirent *entry;

	//get cwd.
	getcwd(cwd,CWD_LENTH);

	//open dir.
	if((dirptr = opendir(cwd)) == NULL){
		perror("ssh");
		exit(EXIT_FAILURE);
	}

	while(entry = readdir(dirptr)){
		//printf("%d\n",entry->d_reclen);
		filenames[number] = entry->d_name;
		number++;
	}

	int i,j=0;


	for (i=0; i<number; i++){
		int count = 0;
		int num = 1;

		//find the '.' or the end of filename. And store their position.
		while(filenames[i][count] != 0){
			if (filenames[i][count] == 46){
				break;
			}
			count++;
		}

		//compare names to aim. if correct, store them.
		while(args[1][num] == filenames[i][count]){
			if (args[1][num] == 0){
				filegets[i]=filenames[i];
				break;
			}
			num++;
			count++;
		}
	}

	//print results.
	for (j=0; j<number; j++){
		if(filegets[j] != NULL){
			printf("%s\n", filegets[j]);
		}
	}

	return 1;
}


这里有很多部分,我一一解释一下。第一段就是函数定义。把要用的函数先声明。第二段是,就是在命令行输入的字符指令。第三段是函数指针,这个格式真的是令人头疼。。。真的,刚开始学指针这部分真的是把我折磨的头皮发麻,每次以为自己懂了结果看到一段新的代码又觉得自己不懂了,,,我已经不敢乱说了,所以这段大家自行百度理解,反正在这里这么写是对的,先这么着吧。第四段就是为了算一下你有几个内建指令,方便在后面用,这样你随便在里面加,后面也不用改,自动就算出来了。理解也很简单,在builtin_str里,每个内存块都占char*个大小,所以除一下就知道有多少个了。最后一段,就是具体实现各个内建指令函数了。

前四个指令的实现真的很简单了,看应该都能看懂,看不懂去百度三十秒搞定。接下来说一个我在教程外自己加的指令search,它可以以“search *.c”的格式搜索当前路径下所有的.c文件。同理别的类型的文件也可以搜索,没有后缀的也可以,写成“search *”就行。主要是利用#include <dirent.h>实现。这个库不在C的标准库中(主要是用标准库我tmd写不出来),但是也是一个非常常用的库。来,接下来我们一点一点看:

if (args[1] == NULL){
        printf("Please input values after 'search'.\n");
        return 1;
    }

这段好理解,就是如果search后面什么都没跟,那就重新进入循环,然后告诉用户重新输入。这个return 1要跟后面综合部分的函数一起看,等我写完我会把源码全部放出来。现在就知道在内建函数中,如果return 1,就是重新进入循环,如果return 0,那就是跳出循环,整个shell都会结束

    char cwd[CWD_LENTH];
    char **filenames = (char **)malloc(sizeof(char *) * 1024);
    char **filegets = (char **)malloc(sizeof(char *) * 1024);
    int number = 0;
    DIR *dirptr = NULL;
    struct dirent *entry;

cwd是用来储存当前路径的。filenames用来放路径下所有文件的文件名。我假设文件夹下不会有超过1024个文件,多了的话,我太菜了,以后再优化吧。filegets用来放搜索出的符合条件的文件名。number用来计数有多少个文件。 最后两个是读取文件所需的函数,后面再说。

    //get cwd.
    getcwd(cwd,CWD_LENTH);

    //open dir.
    if((dirptr = opendir(cwd)) == NULL){
        perror("ssh");
        exit(EXIT_FAILURE);
    }

    while(entry = readdir(dirptr)){
        //printf("%d\n",entry->d_reclen);
        filenames[number] = entry->d_name;
        number++;
    }

第一段,读取当前路径并存在cwd中。第二段,用opendir函数打开当前路径,用dirptr代表(这句话说的不对,但为了新手能够理解,我姑且如此说)。第三段将路径下所有文件名读取出来。entry = readdir(dirptr)会顺序从路径下读取每个文件的信息,每运行一次就会跳到下一个文件。entry->d_name用来读取文件的文件名,entry里面信息很多,这里不一一列举,需要的网上查。

    for (i=0; i<number; i++){
        int count = 0;
        int num = 1;

        //find the '.' or the end of filename. And store their position.
        while(filenames[i][count] != 0){
            if (filenames[i][count] == 46){
                break;
            }
            count++;
        }

        //compare names to aim. if correct, store them.
        while(args[1][num] == filenames[i][count]){
            if (args[1][num] == 0){
                filegets[i]=filenames[i];
                break;
            }
            num++;
            count++;
        }
    }

    //print results.
    for (j=0; j<number; j++){
        if(filegets[j] != NULL){
            printf("%s\n", filegets[j]);
        }
    }

    return 1;
}
这一段就是具体筛选目标文件名的地方!先说一下我的逻辑,把每一个文件名的后缀拿出来,如“.c”或“.txt”,和我们想选的如“.txt”来一位一位的对比,从第一位开始,一样就跳到下一位继续比,比到如果出现NULL,就证明全部相同了!此时就将对应的文件名存到filegets中。注意!为了方便,我没用stack的结构在filegets中存储数据,我直接将filenames的文件名存到对应位置的filegets中。举例,如果filenames中是这样,{“abc.c”,“abc.txt”,“ab.txt”,“main.c”...}我们要选的是.c文件的话,filegets中就是,{“abc.c”,NULL,NULL,“main.c”...}。我们在输出的时候,只要filegets中不是NULL的输出出来就好了。所以我的代码,大循环用来遍历每一个文件名,count用来记录文件名跳到哪一位,num记录目标文件名跳到哪一位。第二段,找到文件名的后缀。第三段,因为输入的是“search *.XXX”,所以从args[1][num]之后记录的就是我们要的,一一比对就好。在二三段要注意的一点是,filenames[x][y]或filegets[x][y]是数!char*型里面的每一位都是数!保存范围用0~255,输出时用ascii码转化成字符。所以用strcmp函数来比较相同与否是行不通的,直接找ascii码用对应数字来判断就好。最后输出就完了。

如何执行外部程序,实现pipe和IO重定向放到下一篇一起讲。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值