一个在终端实现类Linux shell(cd ls命令)UI界面的项目(C语言实现)

20 篇文章 1 订阅
14 篇文章 0 订阅

一个在终端实现类Linux shell(cd ls命令)UI界面的功能(C语言实现)



这2天做了一个类似Linux shell的UI界面,目前已初步完成cd, ls, help, pwd, quit等命令,在Linux下实现,效果图见下图:


ls命令:


开始及help命令:



对于完成此UI界面来说,最主要是根据输入的命令找到相关的命令处理函数。通过参考部分bash的实现,找到了如下方法:

通过一个回调函数指针,实现调用相关命令处理函数的功能,代码如下:

//*****************************************************************************
//
// Command line function callback type.
//
//*****************************************************************************
typedef int (*pfnCmdLine)(int argc, char *argv[]);

//*****************************************************************************
//
//! Structure for an entry in the command list table.
//
//*****************************************************************************
typedef struct
{
    //
    //! A pointer to a string containing the name of the command.
    //
    const char *pcCmd;

    //
    //! A function pointer to the implementation of the command.
    //
    pfnCmdLine pfnCmd;

    //
    //! A pointer to a string of brief help text for the command.
    //
    const char *pcHelp;
}
tCmdLineEntry;



而终端输入的命令最终会在CmdLineProcess()这个核心函数里面进行处理,代码如下:

int CmdLineProcess(char *pcCmdLine)
{
    static char *argv[CMDLINE_MAX_ARGS + 1];
    char *pcChar;
    int argc;
    int bFindArg = 1;
    tCmdLineEntry *pCmdEntry;

    //
    // Initialize the argument counter, and point to the beginning of the
    // command line string.
    //
    argc = 0;
    pcChar = pcCmdLine;

	
	//printf("CmdLineProcess: %s\n", pcCmdLine);
	
    //
    // Advance through the command line until a zero character is found.
    //
    while (*pcChar)
    {
        //
        // If there is a space, then replace it with a zero, and set the flag
        // to search for the next argument.
        //
        if(*pcChar == ' ')
        {
            *pcChar = 0;
            bFindArg = 1;
        }

        //
        // Otherwise it is not a space, so it must be a character that is part
        // of an argument.
        //
        else
        {
            //
            // If bFindArg is set, then that means we are looking for the start
            // of the next argument.
            //
            if(bFindArg)
            {
                //
                // As long as the maximum number of arguments has not been
                // reached, then save the pointer to the start of this new arg
                // in the argv array, and increment the count of args, argc.
                //
                if(argc < CMDLINE_MAX_ARGS)
                {
					//printf("\nargc=%d, argv=%s ", argc, argv);
					//printf("   pcChar=%c ", *pcChar);
					
                    argv[argc] = pcChar;
					
					//printf("\nargc=%d, argv=%s ", argc, argv);
					
                    argc++;
                    bFindArg = 0;
                }

                //
                // The maximum number of arguments has been reached so return
                // the error.
                //
                else
                {
                    return(CMDLINE_TOO_MANY_ARGS);
                }
            }
        }

        //
        // Advance to the next character in the command line.
        //
        pcChar++;
    }

	
	//printf("argc=%d, argv=%s ", argc, argv);
	
    //
    // If one or more arguments was found, then process the command.
    //
    if(argc)
    {
        //
        // Start at the beginning of the command table, to look for a matching
        // command.
        //
        pCmdEntry = &g_sCmdTable[0];
		
		//printf("pCmdEntry->pcCmd=%s \n", pCmdEntry->pcCmd);

        //
        // Search through the command table until a null command string is
        // found, which marks the end of the table.
        //
        while(pCmdEntry->pcCmd)
        {
			//printf("while: pCmdEntry->pcCmd=%s, argv[0]=%s \n", pCmdEntry->pcCmd, argv[0]);
			//printf("while: pCmdEntry->pfnCmd=0x%x \n", pCmdEntry->pfnCmd);		
		
            //
            // If this command entry command string matches argv[0], then call
            // the function for this command, passing the command line
            // arguments.
            //
            if(!strcmp(argv[0], pCmdEntry->pcCmd))
            {
				//printf("Define: pCmdEntry->pcCmd=%s, argv[0]=%s \n", pCmdEntry->pcCmd, argv[0]);
				//printf("***pCmdEntry->pfnCmd=0x%x \n", pCmdEntry->pfnCmd);
				
                return(pCmdEntry->pfnCmd(argc, argv));
            }

            //
            // Not found, so advance to the next entry.
            //
            pCmdEntry++;
        }
    }

    //
    // Fall through to here means that no matching command was found, so return
    // an error.
    //
    return(CMDLINE_BAD_CMD);
}


参考了部分网上的资料,实现Linux "ls" 命令的源码如下:

/************************************************************************************
** File: - Z:\work\code\c\longluo\cmd_ls.c
** VENDOR_EDIT
** Copyright (C), tcpipstack.
** 
** Description: 
**      cmd_ls.c - The implement of the Linux Like "ls" command.
** 
** Version: 1.0
** Date created: 10:20:56,14/11/2012
** Author: long.luo
** 
** --------------------------- Revision History: --------------------------------
** 	<author>	<data>			<desc>
** 
************************************************************************************/

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <pwd.h>
#include <grp.h>
#include <time.h>
#include <dirent.h>

int do_ls(char *dir,char *filename,int lflag)
{
	int n;
	struct stat buf;
	char out[100];
	struct passwd *pw;
	struct group *gr;
	struct tm *t;

    //如果不带l参数,直接显示文件/目录名
	if (lflag == 0)		
	{
		printf("%s\t", filename);
		return 0;
	}
		
	if(lstat(dir,&buf)<0)
	{
		fprintf(stderr,"stat error:%s\n",strerror(errno));
		return -1;
	}

    //获取字符串的属性:普通文件-、目录d、字符设备c、块设备b、
    //管道文件p、连接文件l、套接字文件s
	switch(buf.st_mode & S_IFMT)		
	{
	case S_IFREG:	
		printf("-");
		break;
	case S_IFDIR:	
		printf("d");
		break;
	case S_IFCHR:	
		printf("c");
		break;
	case S_IFBLK:	
		printf("b");
		break;
	case S_IFIFO:	
		printf("p");
		break;
	case S_IFLNK:	
		printf("l");
		break;
	case S_IFSOCK:	
		printf("s");
		break;
	}

    //打印文件的读写属性:读r、写w、执行x、无权限-
	for(n=8;n>=0;n--)		
	{
		if(buf.st_mode&(1<<n))
		{
			switch(n%3)
			{
			case 2:
				printf("r");
				break;
			case 1:
				printf("w");
				break;
			case 0:
				printf("x");
					break;
			default:
				break;
			}
		}
		else
		{
			printf("-");
		}
	}

	//硬链接数,此链接非彼链接,指(包含)目录的个数,
	//文件为1,目录起始为2,再加上目录里包含的目录个数(不递归,只一层)
	printf(" %d",buf.st_nlink);		
	
	pw = getpwuid(buf.st_uid);		//所属用户名
	printf(" %s",pw->pw_name);		

	gr = getgrgid(buf.st_gid);		//所属组名
	printf(" %s",gr->gr_name);

	printf(" %ld",buf.st_size);		//字节计总大小

	t = localtime(&buf.st_atime);	//最后一次访问时间
	printf(" %d-%d-%d %d:%d"
		,t->tm_year+1900
		,t->tm_mon+1
		,t->tm_mday
		,t->tm_hour
		,t->tm_min);
   	printf(" %s ",filename);	

	//判断是否为链接,是返回真   
	if(S_ISLNK(buf.st_mode))		
	{
		printf(" -> ");
		if(readlink(filename,out,100)==-1)
		{
			//printf("readlink error\n");
		}
		printf("%s",out);
	}
	printf("\n");
	
	return 0;
}


// ls的准备工作
int ls_prepare(char *w,int aflag,int lflag)		
{
	struct stat buf;		//man lstat可以看到此结构
	char name[100];	
	DIR *dir;		//类似打开文件的fd描述符
	struct dirent *pdr;		//man readdir可以看到此结构

    //获取文件/目录属性并赋值给buf,该函数和lstat一样,
    //只是当w为链接时,指代他本身,并不存在文件
	if(lstat(w,&buf)<0)		
	{
		fprintf(stderr,"stat error:%s\n",strerror(errno));
		return -1;
	}
	
	if(S_ISDIR(buf.st_mode))	//判断是否为目录,是返回真
	{
		dir = opendir(w);		//打开目录
		while ((pdr = readdir(dir))!=NULL)	//读/遍历目录
		{
			if(aflag==0)	//如果不带a参数,越过以.开头的所有文件/目录
			{ 
				if(pdr->d_name[0]=='.')
					continue;
				memset(name,0,100);		
				strcpy(name,w);			//拷贝
				strcat(name,"/");		//追加
				strcat(name,pdr->d_name);
				do_ls(name,pdr->d_name,lflag);
			}
			//有a参数显示所有
			else		
			{
				memset(name,0,100);
				strcpy(name,w);
				strcat(name,"/");
				strcat(name,pdr->d_name);
				do_ls(name,pdr->d_name,lflag);
			}
		}
		closedir(dir);
	}
	//为文件则直接显示
	else	
	{
		do_ls(w,w,lflag);
	}
	
	return 0;
}


项目的main函数代码如下,主要就是一个while(1)循环读取终端的输入,然后送入命令处理函数,值得注意的是因为输入了换行符,因此需要对输入的字符进行处理,否则会有bug产生:

int main(int argc, char *argv[])
{
    int nStatus, nStrLen;
	
	fprintf(stderr, "Hello, CMD UI Program...");
	
    // Enter an infinite loop for reading and processing commands from the user.
    while (1) {
        // Print a prompt to the console. Show the CWD.
        printf("\n%s> ", g_cCwdBuf);

        // Get a line of text from the user.
        fgets(g_cCmdBuf, CMD_BUF_SIZE, stdin);

        // Remove the char '\n'.
		nStrLen = strlen(g_cCmdBuf);
		if ('\n' == g_cCmdBuf[nStrLen - 1]) {
			g_cCmdBuf[nStrLen - 1] = '\0';
		}
			
        // Pass the line from the user to the command processor.
        // It will be parsed and valid commands executed.
        nStatus = CmdLineProcess(g_cCmdBuf);

        // Handle the case of bad command.
        if(nStatus == CMDLINE_BAD_CMD) {
			printf("Bad command!\n");
        }
        // Handle the case of too many arguments.
        else if(nStatus == CMDLINE_TOO_MANY_ARGS) {
			printf("Too many arguments for command processor!\n");
        }
        // Otherwise the command was executed. Print the error
        // code if one was returned.
        else if(nStatus != 0) {
			printf("Command returned error code.\n");
        }
    }
	
	return 0;
}


在编写这个小项目的同时,也顺便把Makefile的编写也复习了下,这个项目的Makefile文件如下:

#
# Makefile - Rules for building the program.
#	
#	History:
#		long.luo, 12/11/2012, created it.
#		long.luo, 13/11/2012, modified it.
#


# The compiler to be used.
ifndef COMPILER
COMPILER=gcc
endif

# Definitions for using GCC.
ifeq (${COMPILER}, gcc)

# The command for calling the compiler.
CC=gcc

# The flags passed to the compiler.
CFLAGS=-Wall -Os -c

# The command for calling the linker.
LD=ld

# The flags passed to the linker.
LDFLAGS=--gc-sections

# The PHONY targets.	
.PHONY: all clean

# The default rule, which causes the Application to be built.
all: cmd_ui

# The rule to create the target directory.
${COMPILER}:
	mkdir ${COMPILER}

# The rule for building the object file from each C source file.
%.o : %.c
	${CC} ${CFLAGS} -D${COMPILER} -o $@ $<

# The objects.
OBJS=cmdline.o cmd_ui_shell.o cmd_ls.o

# The final executable file.
exec=cmd_ui
	
# The rule for building the application.
cmd_ui: ${OBJS}
	$(CC) -o $@ $?

# The rule to clean out all the build products.
clean:
	rm -f ${wildcard *.o} ${exec}	
	
endif


以上,这个项目可以应用在其他终端上,比如串口上实现简单的UI界面:-)


  • 4
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值