今天我们要讨论的是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: %s\n", ptr->d_ino,ptr->d_off,ptr->d_name);
}
closedir(dir);
return 0;
}
readdir函数按照在磁盘的索引顺序,d_off来排序,若是须要按照文件名d_name,须要遍历后将文件名保存,再次排序。
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: %s\n", 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。 此选项以更长的格式列出目录内容。
上面输出向我们显示目录中所有文件的文件权限、指向该文件的符号链接数量、每个文件的所有者和组、上次修改时间等。
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);
/* 如果使用 getpwuid() 找到所有者的名称,则打印出所有者的名称。 */
if ((pwd = getpwuid(statbuf.st_uid)) != NULL)
printf(" %-8.8s", pwd->pw_name);
else
printf(" %-8d", statbuf.st_uid);
/* 如果使用 getgrgid() 找到组名,则打印出组名。 */
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 %s\n", 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)