(笨方法)利用stat函数实现ls -l filename
学习了一段时间的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函数的使用不大一样,使用方法见谷歌百度。这里主要说明几点:
- s参数必须设置为数组的形式,而不是字符串常量(如:char *str="2,张三,89,99,66″;),因为strtok在执行过程中会对str进行修改,必须保证str是可写的。
- 该函数实际上对参数s的操作:第一次使用该函数的时候将在参数s中向后扫描,找出所有的delim,依次把他们替换为NULL('\0')。
- 第一次它会返回第一个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的更多相关文章
- linux系统编程之文件与IO(六):实现ls -l功能
本文利用以下系统调用实现ls -l命令的功能: 1,lstat:获得文件状态, 2,getpwuid: #include <pwd.h> struct passwd *getpwuid(u ...
- 内存保护机制及绕过方法——利用Ret2Libc绕过DEP之ZwSetInformationProcess函数
1. DEP内存保护机制 1.1 DEP工作原理 分析缓冲区溢出攻击,其根源在于现代计算机对数据和代码没有明确区分这一先天缺陷,就目前来看重新去设计计算机体系结构基本上是不可能的,我们只能靠 ...
- opencv3.2.0实现读取多张图片的方法(利用sprintf()函数)
简介: 将连续的图片转换成视频时,首先需要把图片全部读入,然后再做相应处理,该程序利用sprintf()函数,实现连续图片的读入 /*********新建QT控制台程序,实现多张连续图片的读取**** ...
- SQLServer中利用NTILE函数对数据进行分组的一点使用
本文出处:http://www.cnblogs.com/wy123/p/6908377.html NTILE函数可以按照指定的排序规则,对数据按照指定的组数(M个对象,按照某种排序分N个组)进行分组, ...
- Linux系统编程【3.2】——ls命令优化版和ls -l实现
前情提要 在笔者的上一篇博客Linux系统编程[3.1]--编写ls命令中,实现了初级版的ls命令,但是与原版ls命令相比,还存在着显示格式和无颜色标记的不同.经过笔者近两天的学习,基本解决了这两个问 ...
- 利用JavaScript函数对字符串进行加密
加密.解密问题对我来说一直是很神秘的,感到神奇无比. 理论了解 前段时间看到关于利用JavaScript函数unescape()和escape()对字符串进行替换处理.通过查资料得知, escape( ...
- [置顶] linux第二天,g++,gcc,ps,cat,sort,grep,kill,less,ls -l ,
33.less sample.txt 分页输出文件内容到屏幕 34./search content (搜索内容) 可以将文档中有searchcontent 的行输出到屏幕 35.grep scienc ...
- 利用Format函数格式化时间和日期
在做机房收费系统的时候,因为需要使用到日期进行查询,所以在数据表中没有使用自动添加日期的功能,而是采用了自定义的格式插入.但由于事先没有对时间转换的格式进行统一,导致后面查询时出现的问题不断. 插入时 ...
- (转)Java程序利用main函数中args参数实现参数的传递
Java程序利用main函数中args参数实现参数的传递 1.运行Java程序的同时,可以通过输入参数给main函数中的接收参数数组args[],供程序内部使用!即当你在Java命令行后面带上参数,J ...
随机推荐
- [UE4]Replications,复制
关于进程 1.进程:运行中的程序 虚幻4游戏进程的四种网络模式 1.StandAlone:单机模式,不联网 2.Client,网络游戏中的客户端. 3.ListenServer,服务器和一个客户端 4 ...
- 做好平衡有多难?谈MMO的职业设计
转自:http://www.gameres.com/804893.html 首先要明确个概念:平衡不是在YY好的职业设计基础上去做调整,而是从游戏设计的开始就要打造一套有标准.可调节的游戏设计框架. ...
- Linux双网卡绑定
Linux双网卡绑定 作者:Eric 微信:loveoracle11g eth0和eth1绑定为bond0 [root@rac-node1 ~]# cat /etc/sysconfig/network ...
- L1正则化
正则化项本质上是一种先验信息,整个最优化问题从贝叶斯观点来看是一种贝叶斯最大后验估计,其中正则化项对应后验估计中的先验信息,损失函数对应后验估计中的似然函数,两者的乘积即对应贝叶斯最大后验估计的形式, ...
- SQL按分隔符拆分字段串
CREATE VIEW [dbo].[Split_BusinessUnit] AS WITH tt AS ( SELECT BusinessUnit.BusinessUnitId , Business ...
- C++ Templates编程(模板参数)
//file max.hpp template <typename T> //template<class T> inline T const& max (T cons ...
- 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 ...
- Hive数据据类型 DDL DML
Hive的基本数据类型 DDL DML: 基本数据类型 对于Hive而言String类型相当于数据库的varchar类型,该类型是一个可变的字符串,不过它不能声明其中最多能存储多少个字符,理论上它可以 ...
- 安装MySQL半同步复制
一.简介 从MySQL5.5开始,MySQL以插件的形式支持半同步复制.如何理解半同步呢?首先我们来看看异步,全同步的概念 异步复制(Asynchronous replication) MySQL默认 ...
- solr defType查询权重排序
Solr的defType有dismax/edismax两种,这两种的区别,可参见:http://blog.csdn.net/duck_genuine/article/details/8060026 下 ...