博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
(笨方法)利用stat函数实现ls -l filename
阅读量:5098 次
发布时间:2019-06-13

本文共 7475 字,大约阅读时间需要 24 分钟。

学习了一段时间的Linux了,但是我感觉我做不出来啥子,后头选择利用系统IO函数实现命令,先从ls走起吧。先来看看ls -l filename给我们显示了什么吧 :

可以看到,一共八项:文件类型、用户权限、文件硬连接数目、文件所有者、文件所属组、占用空间大小、文件修改日期、文件名。下面我们一个一个实现他们。但在此之前,我们需要了解一下ls需要用到的xitongio函数:stat();该函数的原型:int stat(const char *pathname, struct stat *buf);第一个参数是指针,指向文件名字符串。第二个是一个stat结构体。文件名字符串可以在运行的时候输入,但是stat结构体需要我们自己定义。所以我定义了一个全局变量:static struct stat st;接下来我们来看看stat中包含了哪些内容:

可以知道,ls -l需要的基本上都有。现在我们来声明函数

void file_typeAnd_permissions(char *file);//获取文件类型和文件权限

void File_hard_connection_number(void);//文件的硬连接数目
void File_owner(int id);//文件所有者
void Group_of_files(void);//文件所属组
void File_size(void);//文件大小
void File_modification_time(void);//文件修改时间
void File_name(char *file);//文件名
char* itoa(int num,char *str,int radix);//进制转换,我们需要将st_mode转化为8进制数

刚才我提到了一个st_mode,由上面的图片我们知道,st_mode是文件类型和文件存取的权限;它一个变量包含了多个信息,那么它自身就不简单了,我们可以继续看:

可以看到,st_mode一共有16位。那么我们怎么知道这个文件的类型呢?没事,Linux早就定义好了一些宏(在man文档中可以找到)供我们使用,接下来上函数:

void file_typeAnd_permissions(char *file)

{

    char num[16] = { '\0' };

 

    int s_ret = stat(file, &st);

    itoa((int)st.st_mode, num, 8);//将st.st_mode转化为八进制数,用于判断权限。

                                 //注意,itoa函数b并不是c标准库函数,在其他编译平台或许支持,但是gcc就是不认它。我们可以编写一个itoa函数,而他的用法可以百度一下,或者看声明

                                 //注:此函数并不由我编写,我是去百度百科上面找的

 

                                 // printf(" st_mode=%o num=%s ",st.st_mode,num);

                                 // printf("sizeof(num)=%lu",sizeof(num));

 

    if (-1 == s_ret)//做失败处理措施

    {

        perror("stat");

        exit(1);

    }

    //利用宏来判断文件类型

    if (S_ISLNK(st.st_mode))

    {

        printf("符号链接文件 ");

    }

    else if (S_ISREG(st.st_mode))

    {

        printf("一般文件 ");

    }

    else if (S_ISDIR(st.st_mode))

    {

        printf("目录文件 ");

    }

    else if (S_ISCHR(st.st_mode))

    {

        printf("字符设备文件 ");

    }

    else if (S_ISBLK(st.st_mode))

    {

        printf("块设备文件 ");

    }

    else if (S_ISSOCK(st.st_mode))

    {

        printf("socket文件 ");

    }

    else if (S_ISFIFO(st.st_mode))

    {

        printf("管道文件 ");

    }

 

    //判断权限

    int bit_num = 3;//控制在i的基础上加几位.我在查看一个目录的时候,发现st_mode转化的八进制数只有5位,这是前面有一位被转化之后出现的合并。具体原因可以百度,这里不做解释。毕竟st_mode的是十六位

    bit_num -= (6 - strlen(num));

 

    for (int i = 0; i != 3; i++)

    {

        switch (num[bit_num + i])

        {

        case '0':printf("无权限 "); break;

        case '1':printf("执行 "); break;

        case '2':printf("写 "); break;

        case '3':printf("执行写 "); break;

        case '4':printf("读 "); break;

        case '5':printf("执行读 "); break;

        case '6':printf("读写 "); break;

        case '7':printf("执行读写"); break;

        default:printf("该位对应的值:%d。无匹配操作! ", num[3 + i]);

        }

    }

}

如何查看权限,我是将st_mode转化为八进制数,然后查找后三位来实现的。其中用到的itoa函数不是c标准库中的函数,在Linux中使用gcc的话不会认可它的,所以我就自己编写了一个。strlen函数需要头文件string.h。由于st_mode中包含了文件类型和文件权限。所以我们就一个函数解决两个问题了,但是我还是建议不要这样,一个函数最好是解决一个问题。接下来就是硬链接数目了,但是要注意,目录的硬连接数目初始为0;文件的硬链接数目初始为1;

void File_hard_connection_number(void)

{

    printf("%d", (int)st.st_nlink);

}

这个比较简单,直接输出就是。然后我们来看看所属用户和所属组了,我们能直接用stat函数获取到用户id(uid)和组id(gid),但是这并不是我们想要的,我们还是习惯于看字符串。但是id和passwd文件有对应关系,passwd文件在/etc目录中。我们来看看psswd文件内容是什么样的:

可以知道,每一行当中,用户名、用户id、组id之间有":"分隔开的。知道这个之后,我们就可以获取了。函数实例:

void File_owner(int id)//这样设计是用来找到uid和gid一样的字符串

{

    //知道了用户id之后,我们就可以到目录/etc中去找到文件passwd找寻对应的用户名和组名

    FILE *fd = fopen(" / etc / passwd", "r"); //以只读方式打开passwd文件,也可以用系统io函数open,read函数的,但是不大好控制,所以就选择了c库的文件操作函数

    int i = 0; //用于标记读取到了几个':',好做判断。至于为什么要这么做,可以打开passwd文件看看每一行的格式就明白了。

    int Break = 0; //当我们在文件中间位置找到了我们需要的数据后,用于控制循环退出的一个变量

    char pass[512] = { 0 }; //存储每一行的数据

    char p[30] = { '\0' }; //用于存储id,与id对比

 

                                             // printf("uid=%d gid=%d ",st.st_uid,st.st_gid);

    if (NULL == fd)//处理文件操作出错的代码必不可少,也要记得,打开就要记得关闭

    {

        printf("打开文件失败\n");

        exit(1);

    }

    while ((fgets(pass, 500, fd)) && (!Break))//gfgets函数用于读取一行数据,具体的用法,可百度

    {

        for (int j = 0, i_ = 0; i_ != 70; i_++)

        {

            //j控制p

            if (':' == pass[i_])

            {

                i++;

            }

            if (2 == i)//此时意味着我们找到了当前行的id

            {

                if (':' != pass[i_])

                {

                    p[j] = pass[i_];

                    j++;

                }

            }

            if (3 == i)

            {

                // printf("进入");

                int a = atoi(p);//将字符串转化为数字的一个函数

                                // printf("a=%d p=%s ",a,p);

                if (a == id)//若用户id和id相等,此时我们就要舍弃p原有的数据,然后用来存储用户(组)名了

                {

                    // printf("进入");

                    for (int i = 0; i != 30; i++)//将p恢复

                    {

                        p[i] = '\0';

                    }

                    Break = 1;

                    int i = 0;

                    while (pass[i] != ':')

                    {

                        p[i] = pass[i];

                        i++;

                    }

                    printf("%s ", p);//然后输出用户(组)名

                    break;

                }

            }

 

        }

        i = 0;//在此行没找到

        for (int i = 0; i != 30; i++)//没找到,为了避免在找到之前有的idb'ni'wo'men比我们的id长,那么必须进行清除操作

        {

            p[i] = '\0';

        }

    }

    fclose(fd);

}

 

void Group_of_files(void)

{

    //当用户id和组id一样的时候,那么用户名和组名一样,若是uid不等于gid那么我我们就要去找寻uid和此时的gid一样的用户。这也是为何上一个函数要有一个id参数的原因

    if (st.st_uid == st.st_gid)

    {

        File_owner(st.st_uid);

    }

    else

    {

        File_owner(st.st_gid);

    }

}

//oh,真的,我去试过了strtok,但是我总是得到一个段错误。这让我很是气馁。不过还好,我找到错误了,真的,这个错误让我很心累:就是我错误的把:弄成了; 先上代码:

    void File_owner(int id)//这样设计是用来找到uid和gid一样的字符串

{

    //知道了用户id之后,我们就可以到目录/etc中去找到文件passwd找寻对应的用户名和组名

    FILE *fd = fopen(" / etc / passwd", "r"); //以只读方式打开passwd文件,也可以用系统io函数open,read函数的,但是不大好控制,所以就选择了c库的文件操作函数

    int i = 0; //用于标记读取到了几个':',好做判断。至于为什么要这么做,可以打开passwd文件看看每一行的格式就明白了。

    int Break = 1; //当我们在文件中间位置找到了我们需要的数据后,用于控制循环退出的一个变量

    char pass[] = { 0 }; //存储每一行的数据

    char *p_1; //用于存储用户名

    char *p_2; //用于存储不需要的字符串

    char *p_3; //存储uid,与id参数对比

    if (NULL == fd)//处理文件操作出错的代码必不可少,也要记得,打开就要记得关闭

    {

        printf("打开文件失败\n");

        exit(1);

    }

    //char *strtok(char s[], const char *delim);//strtok的原型

    while (fgets(pass, , fd))

    {

        p_1 = strtok(pass, ":");

        p_2 = strtok(NULL, ":");

        p_3 = strtok(NULL, ":");

        int pid = atoi(p_3);

        if (pid == id)

        {

            printf("%s ", p_1);

            break;

        }

    }

}

strtok函数的使用不大一样,使用方法见谷歌百度。这里主要说明几点:

  1. s参数必须设置为数组的形式,而不是字符串常量(如:char *str="2,张三,89,99,66″;),因为strtok在执行过程中会对str进行修改,必须保证str是可写的。
  2. 该函数实际上对参数s的操作:第一次使用该函数的时候将在参数s中向后扫描,找出所有的delim,依次把他们替换为NULL('\0')。
  3. 第一次它会返回第一个delim前面的字符串,第二次需要让他返回第二个串的话就要将参数s置为NULL。

用库函数明显优于我们自己造轮子,以后还是尽可能的使用库函数。我获取组名是依靠了它:void File_owner(int id)的,我的想法是,uid=gid的时候,用户名和组名就相同,可以理解为,此时的用户名就是组名;获取组名的时候,若是uid=gid,那么好说;若不是,那么我们就要去找到另一个uid等于我们的st_gid的用户名。

下面就是文件的大小了,很简单,这个:

void File_size(void)//size的大小可以直接输出。

{

    printf("%d ", (int)st.st_size);

}

然后是文件的修改日期,要注意,这里使用时间函数,需要头文件time.h

void File_modification_time(void)//输出时间需要头文件time.h。

{

    char *my_time = ctime(&st.st_mtime);//ctime函数会自动在字符串后面加上一个换行符。但是这不符合ls -ld命令的输出形式。所以要做处理

    int i = 0;

    while (my_time[i] != '\n')

    {

        i++;

    }

    my_time[i] = '\0';

    // printf("%lu\n",st.st_mtime);//打印time_th格式的时间,s单位为s

    printf("%s ", my_time);//打印ctimeh格式的时间,为字符串

                            // printf("%s\n",asctime(localtime(&st.st_mtime)));//打印sasctime格式的时间,这行和上一行效果一样

}

几种时间函数我已经试过了,就看大家使用什么了。

然后是输出文件名,这也简单,不过要记住输出的时候就该换行了:

void File_name(char *file)

{

    printf("%s\n", file);

}

itoa函数的实现我还是贴出来吧,要注意的是,itoa函数不是C标准库的函数,gcc不认它:

char* itoa(int num, char*str, int radix)

{

/*索引表*/

    char index[] = "0123456789ABCDEF";

    unsigned unum;/*中间变量*/

    int i = 0, j, k;

    /*确定unum的值*/

    if (radix == 10 && num<0)/*十进制负数*/

    {

        unum = (unsigned)-num;

        str[i++] = ' - ';

    }

    else unum = (unsigned)num;/*其他情况*/

                             /*转换*/

    do {

        str[i++] = index[unum % (unsigned)radix];

        unum /= radix;

    } while (unum);

    str[i] = '\0';

    /*逆序*/

    if (str[0] == ' - ')k = 1;/*十进制负数*/

    else k = 0;

    char temp;

    for (j = k; j <= (i - 1) / 2; j++)

    {

        temp = str[j];

        str[j] = str[i - 1 + k - j];

        str[i - 1 + k - j] = temp;

    }

    return str;

}

接下来的工作就很简单了,直接在main函数中调用就是:

#include <stdio.h>

#include <time.h>

#include <string.h>

#include <stdlib.h>

#include <sys/types.h>

#include <sys/stat.h>

#include <unistd.h>

//int stat(const char *file_name, struct stat *buf )函数原型

void file_typeAnd_permissions(char *file);

void File_hard_connection_number(void);

void File_owner(int id);

void Group_of_files(void);

void File_size(void);

void File_modification_time(void);

void File_name(char *file);

char* itoa(int num, char *str, int radix);//进制转换

static struct stat st;

int main(int a, char *file[])

{

    if (a<2)

    {

        printf("参数过少!\n");

        exit(1);

    }

    file_typeAnd_permissions(file[1]);

    File_hard_connection_number();

    File_owner(st.st_uid);

    Group_of_files();

    File_size();

    File_modification_time();

    File_name(file[1]);

    return 0;

}

好了,接下来就编译他们就是了:gcc my_LS.c -o LS

使用 ./LS my_LS.c

部分图片非原创,侵权请告知,方便处理。

转载于:https://www.cnblogs.com/love-DanDan/p/8723394.html

你可能感兴趣的文章
查询消除重复行
查看>>
[leetcode]Minimum Path Sum
查看>>
内存管理 浅析 内存管理/内存优化技巧
查看>>
Aizu - 1378 Secret of Chocolate Poles (DP)
查看>>
csv HTTP简单表服务器
查看>>
IO流写出到本地 D盘demoIO.txt 文本中
查看>>
Screening technology proved cost effective deal
查看>>
mysql8.0.13下载与安装图文教程
查看>>
Thrift Expected protocol id ffffff82 but got 0
查看>>
【2.2】创建博客文章模型
查看>>
Kotlin动态图
查看>>
从零开始系列之vue全家桶(1)安装前期准备nodejs+cnpm+webpack+vue-cli+vue-router
查看>>
Jsp抓取页面内容
查看>>
大三上学期软件工程作业之点餐系统(网页版)的一些心得
查看>>
可选参数的函数还可以这样设计!
查看>>
[你必须知道的.NET]第二十一回:认识全面的null
查看>>
Java语言概述
查看>>
关于BOM知识的整理
查看>>
使用word发布博客
查看>>
面向对象的小demo
查看>>