学习了一段时间的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

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

(笨方法)利用stat函数实现ls -l filename的更多相关文章

  1. linux系统编程之文件与IO(六):实现ls -l功能

    本文利用以下系统调用实现ls -l命令的功能: 1,lstat:获得文件状态, 2,getpwuid: #include <pwd.h> struct passwd *getpwuid(u ...

  2. 内存保护机制及绕过方法——利用Ret2Libc绕过DEP之ZwSetInformationProcess函数

    1.    DEP内存保护机制 1.1   DEP工作原理 分析缓冲区溢出攻击,其根源在于现代计算机对数据和代码没有明确区分这一先天缺陷,就目前来看重新去设计计算机体系结构基本上是不可能的,我们只能靠 ...

  3. opencv3.2.0实现读取多张图片的方法(利用sprintf()函数)

    简介: 将连续的图片转换成视频时,首先需要把图片全部读入,然后再做相应处理,该程序利用sprintf()函数,实现连续图片的读入 /*********新建QT控制台程序,实现多张连续图片的读取**** ...

  4. SQLServer中利用NTILE函数对数据进行分组的一点使用

    本文出处:http://www.cnblogs.com/wy123/p/6908377.html NTILE函数可以按照指定的排序规则,对数据按照指定的组数(M个对象,按照某种排序分N个组)进行分组, ...

  5. Linux系统编程【3.2】——ls命令优化版和ls -l实现

    前情提要 在笔者的上一篇博客Linux系统编程[3.1]--编写ls命令中,实现了初级版的ls命令,但是与原版ls命令相比,还存在着显示格式和无颜色标记的不同.经过笔者近两天的学习,基本解决了这两个问 ...

  6. 利用JavaScript函数对字符串进行加密

    加密.解密问题对我来说一直是很神秘的,感到神奇无比. 理论了解 前段时间看到关于利用JavaScript函数unescape()和escape()对字符串进行替换处理.通过查资料得知, escape( ...

  7. [置顶] linux第二天,g++,gcc,ps,cat,sort,grep,kill,less,ls -l ,

    33.less sample.txt 分页输出文件内容到屏幕 34./search content (搜索内容) 可以将文档中有searchcontent 的行输出到屏幕 35.grep scienc ...

  8. 利用Format函数格式化时间和日期

    在做机房收费系统的时候,因为需要使用到日期进行查询,所以在数据表中没有使用自动添加日期的功能,而是采用了自定义的格式插入.但由于事先没有对时间转换的格式进行统一,导致后面查询时出现的问题不断. 插入时 ...

  9. (转)Java程序利用main函数中args参数实现参数的传递

    Java程序利用main函数中args参数实现参数的传递 1.运行Java程序的同时,可以通过输入参数给main函数中的接收参数数组args[],供程序内部使用!即当你在Java命令行后面带上参数,J ...

随机推荐

  1. [UE4]Replications,复制

    关于进程 1.进程:运行中的程序 虚幻4游戏进程的四种网络模式 1.StandAlone:单机模式,不联网 2.Client,网络游戏中的客户端. 3.ListenServer,服务器和一个客户端 4 ...

  2. 做好平衡有多难?谈MMO的职业设计

    转自:http://www.gameres.com/804893.html 首先要明确个概念:平衡不是在YY好的职业设计基础上去做调整,而是从游戏设计的开始就要打造一套有标准.可调节的游戏设计框架. ...

  3. Linux双网卡绑定

    Linux双网卡绑定 作者:Eric 微信:loveoracle11g eth0和eth1绑定为bond0 [root@rac-node1 ~]# cat /etc/sysconfig/network ...

  4. L1正则化

    正则化项本质上是一种先验信息,整个最优化问题从贝叶斯观点来看是一种贝叶斯最大后验估计,其中正则化项对应后验估计中的先验信息,损失函数对应后验估计中的似然函数,两者的乘积即对应贝叶斯最大后验估计的形式, ...

  5. SQL按分隔符拆分字段串

    CREATE VIEW [dbo].[Split_BusinessUnit] AS WITH tt AS ( SELECT BusinessUnit.BusinessUnitId , Business ...

  6. C++ Templates编程(模板参数)

    //file max.hpp template <typename T> //template<class T> inline T const& max (T cons ...

  7. hadoop机群 运行wordcount出现 Input path does not exist: hdfs://ns1/user/root/a.txt

    机群搭建好,执行自带wordcount时出现: Input path does not exist: hdfs://ns1/user/root/a.txt 此错误. [root@slave1 hado ...

  8. Hive数据据类型 DDL DML

    Hive的基本数据类型 DDL DML: 基本数据类型 对于Hive而言String类型相当于数据库的varchar类型,该类型是一个可变的字符串,不过它不能声明其中最多能存储多少个字符,理论上它可以 ...

  9. 安装MySQL半同步复制

    一.简介 从MySQL5.5开始,MySQL以插件的形式支持半同步复制.如何理解半同步呢?首先我们来看看异步,全同步的概念 异步复制(Asynchronous replication) MySQL默认 ...

  10. solr defType查询权重排序

    Solr的defType有dismax/edismax两种,这两种的区别,可参见:http://blog.csdn.net/duck_genuine/article/details/8060026 下 ...