Linux cc++下ls命令的实现(超详细)

Linux cc++下ls命令的实现(超详细),第1张

Linux c/c++下ls命令的实现(超详细)

今天我们要讨论的是bash shell的一个重要命令,就是“ls”命令。Linux 中的 ls 命令是每个 Linux 用户都应该知道的最重要的命令之一。如果您是使用命令行的初学者,ls 可能是您应该尝试学习的第一个命令。

ls 是 list 的缩写,用于列出当前工作目录或其他目录(如果指定)中的文件

ls命令为什么这么重要

ls 如此重要的原因在于它允许查看目录中的文件。将经常使用它来列出目录内容。 ls 不是一个复杂的命令,实现起来也很容易,但它确实包含许多不同的选项,可用于列出包含附加信息的文件。

在这过程中,你可能会发现其中一些选项非常有用,即使 ls 本身总是足以列出内容。掌握 ls 命令将使你更有效地列出目录内容和查找文件。它也可以在 Bash 脚本中使用,以帮助其他工具 *** 作文件。

最后,我们每天在使用linux的时候经常使用 ls 命令,但是你了解ls的实现吗?

快速了解ls命令

ls 命令列出目录中包含的文件和子目录。 您可以与 ls 一起使用的选项主要是为了列出附加信息,或者以不同的方式格式化输出。


ls -l显示文件或目录、大小、修改日期和时间、文件或文件夹名称和文件所有者及其权限。

其他选项这里就不列举出来了,感兴趣的可以通过man手册了解。

怎么实现ls命令?

我们已经通过在命令行上输入ls命令,列出目录中包含的文件和子目录。


-a 选项还将列出隐藏文件(名称以 . 开头的文件)。 除非您在根目录中,否则它还会列出 . (当前工作目录)和 … (向上一个目录)作为文件。

那么我们如何读取目录、文件信息呢?

#include
struct dirent *readdir(DIR *dirp);

最多常见的readdir使用方式:

#include
#include
#include
#include 
#include 
#include 

using namespace std;

#define FILE_NAME "/opt/code/linux_command_code"

int main(int argc, char **argv)
{
	DIR   *dir;
	struct  dirent *ptr;
	dir = opendir(FILE_NAME);
	if(NULL == dir)
	{
		cout << "opendir is NULL" << endl;
		return -1;
	}
	while( (ptr = readdir(dir))!=NULL)
	{
		printf("d_ino:%ld  d_off:%ld d_name: %sn", ptr->d_ino,ptr->d_off,ptr->d_name);   
	}
	closedir(dir);
	
	return 0;
}

编译运行:

上面输出是未按照文件名排序。

readdir函数按照在磁盘的索引顺序,d_off来排序,若是须要按照文件名d_name,须要遍历后将文件名保存,再次排序。

  • scandir 的使用
int scandir(const char *dir,struct dirent **namelist,int (*filter)(const void *b),
                       int ( * compare )( const struct dirent **, const struct dirent ** ) );
int alphasort(const void *a, const void *b);
int versionsort(const void *a, const void *b);

函数scandir扫描dir目录下以及dir子目录下满足filter过滤模式的文件,返回的结果是compare函数经过排序的,并保存在 namelist中。注意namelist是通过malloc动态分配内存的,所以在使用时要注意释放内存。alphasort和versionsort 是使用到的两种排序的函数。

常见scandir的使用:

#include
#include
#include
#include 
#include 
#include 

using namespace std;

#define FILE_NAME "/opt/code/linux_command_code"

int main(int argc, char **argv)
{
	struct dirent **namelist;
	int n;
	n = scandir(FILE_NAME,&namelist,0,alphasort);
	if(n < 0)
	{ 
		cout << "scandir return "<< n  << endl;
	}
	else
	{
		int index=0;
		while(index < n)
		{
			printf("d_ino:%ld  d_off:%ld d_name: %sn", namelist[index]->d_ino,namelist[index]->d_off,namelist[index]->d_name);
			free(namelist[index]);
			index++;
		}
		free(namelist);
	}
	
	return 0;
}

编译运行:

scandir函数中能够直接调用排序函数,将遍历到的文件名按照顺序保存在队列中。

下面我们来看看怎么实现ls -l ;

如何实现 ls -l

在 Linux 中与 ls 命令一起使用的最常用选项之一是 -l。 此选项以更长的格式列出目录内容。

上面输出向我们显示目录中所有文件的文件权限、指向该文件的符号链接数量、每个文件的所有者和组、上次修改时间等。

下面是定义的7个字段。

char * 	getperm	(char * , struct stat filestat);
int    	getlinks(struct stat filestat);
char * 	getuser	(struct stat filestat);
char *	getgroup(struct stat filestat);
int	getsize	(struct stat filestat);
char *	getdate	(char * , struct stat filestat);
char *	getname	(char * , char * , char , int);

获得文件信息

stat 能获取与文件系统及文件相关的许多信息,具体用途见stat的功能选项。这些信息包括inode、atime、ctime、mtime、文件(系统)类型、权限、块大小、符号连接等。

下面我来实现 ls -l,具体代码如下:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

using namespace std;

#define FILE_NAME "/opt/code/linux_command_code/ls/stat_test"
char * getperm(char * perm , struct stat fileStat);


int main(int argc , char * argv[])
{
	DIR   *dir;
	struct dirent  *dp;
	struct stat     statbuf;
	struct passwd  *pwd;
	struct group   *grp;
	struct tm      *tm;
	char            datestring[256];
	char modestr[11];
	
	dir = opendir(FILE_NAME);
	if(NULL == dir)
	{
		cout << "opendir is NULL" << endl;
		return -1;
	}
	
	while ((dp = readdir(dir)) != NULL) 
	{
		
		if (stat(dp->d_name, &statbuf) == -1)
			continue;

		
		printf("%10.10s", getperm(modestr,statbuf));
		printf("%4d", statbuf.st_nlink);

		
		if ((pwd = getpwuid(statbuf.st_uid)) != NULL)
		printf(" %-8.8s", pwd->pw_name);
		else
		printf(" %-8d", statbuf.st_uid);

		
		if ((grp = getgrgid(statbuf.st_gid)) != NULL)
		printf(" %-8.8s", grp->gr_name);
		else
		printf(" %-8d", statbuf.st_gid);

		
		printf(" %9jd", (intmax_t)statbuf.st_size);

		tm = localtime(&statbuf.st_mtime);

		
		strftime(datestring, sizeof(datestring), nl_langinfo(D_T_FMT), tm);

		printf(" %s %sn", datestring, dp->d_name);
	}	
	
	return 0;
}


char * getperm(char * perm , struct stat fileStat) {


	if ( S_ISLNK(fileStat.st_mode) ) {
		perm[0] = 'l';
	}
	else if ( S_ISDIR(fileStat.st_mode) ) {
		perm[0] = 'd';
	}
	else if ( S_ISCHR(fileStat.st_mode) ) {
		perm[0] = 'c';
	}
	else if ( S_ISSOCK(fileStat.st_mode) ) {
		perm[0] = 's';
	}
	else if ( S_ISFIFO(fileStat.st_mode) ) {
		perm[0] = 'p';
	}
	else if ( S_ISBLK(fileStat.st_mode) ) {
		perm[0] = 'b';
	}
	else {
		perm[0] = '-';
	}
	perm[1] = ((fileStat.st_mode & S_IRUSR) ? 'r' : '-');
	perm[2] = ((fileStat.st_mode & S_IWUSR) ? 'w' : '-');
	perm[3] = ((fileStat.st_mode & S_IXUSR) ? 'x' : '-');
	perm[4] = ((fileStat.st_mode & S_IRGRP) ? 'r' : '-');
	perm[5] = ((fileStat.st_mode & S_IWGRP) ? 'w' : '-');
	perm[6] = ((fileStat.st_mode & S_IXGRP) ? 'x' : '-');
	perm[7] = ((fileStat.st_mode & S_IROTH) ? 'r' : '-');
	perm[8] = ((fileStat.st_mode & S_IWOTH) ? 'w' : '-');
	perm[9] = ((fileStat.st_mode & S_IXOTH) ? 'x' : '-');

	if ( fileStat.st_mode & S_ISUID ) {
		perm[3] = 's';
	}
	else if ( fileStat.st_mode & S_IXUSR ) {
		perm[3] = 'x';
	}
	else {
		perm[3] = '-';
	}

	if ( fileStat.st_mode & S_ISGID ) {
		perm[6] = 's';
	}
	else if ( fileStat.st_mode & S_IXGRP ) {
		perm[6] = 'x';
	}
	else {
		perm[6] = '-';
	}

	if ( fileStat.st_mode & S_ISVTX ) {
		perm[9] = 't';
	}
	else if ( fileStat.st_mode & S_IXOTH ) {
		perm[9] = 'x';
	}
	else {
		perm[9] = '-';
	}

	perm[10] = 0;

	return perm;
}

编译运行:

  • 权限

针对文件:r-查看,w-修改,x-执行
针对文件夹:r-列出文件夹下的所有文件和文件夹,w-在目录中创建和删除,x-进入目录

权限数字说明:权限字母组合转换为二进制1,0组合,有字母的位用1表示,-的位用0表示,然后转换为十进制数字。如:

rwx组合对应的二进制为 111,转换为十进制7
rw-组合对应的二进制为 110,转换为十进制6
r-x组合对应的二进制为 101,转换为十进制5

在struct stat中,文件所有者都是以ID形式存在的,代码中输出用户名和组名。主要使用了该函数:

char * getuser(struct stat fileStat) 
{
	struct passwd *pass = getpwuid(fileStat.st_uid);
	return pass->pw_name;
}

char * getgroup(struct stat fileStat) 
{
	struct group *pass = getgrgid(fileStat.st_gid);
	return pass->gr_name;
}

总的来说,实现“ls -l”功能所涉及的特殊结构体较多,基础知识考察较多,需要注意细节。逻辑结构上算是很简单,没有什么需要特别留意的地方。

ls命令具体选项实现

使用 -t 选项按修改时间对文件进行排序。 这会将最近编辑的文件带到输出的顶部,使它们更容易找到。


使用 -R 选项递归列出目录的内容。 这意味着每个子目录的内容也将被列出。

int main(int argc , char * argv[]) {

	char c = 0;
	char ** arg = NULL;
	int count_arg = 0;

	options_t * opt = (options_t *) calloc(1 , sizeof(options_t));

	while ( (c = getopt(argc, argv, "aSdltR")) != -1) {
		
		switch (c) {
			case 'a':
				opt->flag_a = 1;
		     		break;
			case 'S':
				opt->flag_S = 1;
				opt->flag_t = 0;
		     		break;
			case 'd':
				opt->flag_d = 1;
		     		break;
			case 'l':
				opt->flag_l = 1;
				break;
			case 't':
				opt->flag_t = 1;
				opt->flag_S = 0;
		     		break;
			case 'R':
				opt->flag_R = 1;
		     		break;
			case '?':
			    	break;
			default:
			    	printf ("?? getopt returned character code 0%o ??n", c);
		}
    }
    if (optind < argc) {
		arg = (char **) calloc ( (argc - optind) , sizeof(char *) );
		count_arg = 0;
		while (optind < argc) {
			arg[count_arg++] = argv[optind++];
		}
    }
	
	ls(opt , arg , count_arg);

	if( arg != NULL )
		free( arg );
	free(opt);

	return 0;

}

编译运行

总结

在 Unix/Linux 的文件系统中,所有东西的储存形式都是文件(一切皆文件的理念)。ls命令是linux下最常用的命令之一,它的使用很简单,可是功能却很多,有很多的参数,本篇文章中给出了一些测试例子,方便理解ls命令。

欢迎关注微信公众号【程序猿编码】,需要ls命令源码的添加本人微信号(c17865354792)

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/4653103.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-11-06
下一篇 2022-11-06

发表评论

登录后才能评论

评论列表(0条)

保存