Linux命令基础——stat-readdir-dup2

在学习Linux命令基础总结了笔记,并分享出来。有问题请及时联系博主:Alliswell_WP,转载请注明出处。

08-linux-day04(stat-readdir-dup2)

目录:
一、学习目标
二、文件和目录操作
1、打开最大文件数量
2、stat函数介绍
3、stat函数介绍2与stat命令
4、实现ls -l命令
5、stat与lstat的区别
6、access与truncate
7、readlink、unlink
8、unlink补充
9、chown与rename
10、chdir、getcwd切换目录和获得工作路径
11、mkdir创建目录
12、读目录相关函数介绍
13、递归子目录统计普通文件个数
14、errno说明
15、dup2和dup说明
16、dup2和dup的使用


一、学习目标

1、掌握stat/lstat函数的使用

2、了解文件属性相关的函数使用

3、了解目录操作相关的函数的使用

4、掌握目录遍历相关函数的使用

5、掌握dup、dup2函数的使用

6、掌握fcntl函数的使用

 


虚函数地址空间


 

二、文件和目录操作

1、打开最大文件数量


### xxx.c ---> xxx

>touch makefile

>vi makefile

 1 ### xxx.c ---> xxx
 2 SrcFiles=$(wildcard *.c)
 3 TargetFiles=$(patsubst %.c,%,$(SrcFiles))
 4 
 5 all:$(TargetFiles)
 6 
 7 %:%.c
 8     gcc -o $@ $^ -g
 9     
10 clean:
11     rm -f $(TargetFiles)

>touch openmax.c

>vi openmax.c

 1 #include<stdio.h>
 2 #include<sys/types.h>
 3 #include<sys/stat.h>
 4 #include<fcntl.h>
 5 #include<unistd.h>
 6 
 7 int main()
 8 {
 9     int num = 3;
10     char filename[128]={0};
11     while(1)
12     {
13         sprintf(filename,"temp_%04d",num++);
14         if(open(filename,O_RDONLY|O_CREAT,0666) < 0)
15         {
16             perror("open error");
17             break;
18         }
19     }
20     printf("num == %d\n", num);
21     return 0;
22 }

>make

>./openmax

open err:Too many open files

num == 1025

 

2、stat函数介绍

》man 2 stat

  int stat(const char* pathname, struct stat *buf);


存储原理:

>touch hello

>vi hello

(随便输入内容后保存退出)

>ln hello hello.hard

>ls -lrt

>ls -i hello*

输出:2642317 hello 2642317 hello.hard(ls -i 指定文件——查看指定文件的索引号

 

 


查找timespec结构体

>sudo grep -rn "struct timespec {" /usr/

查到time.h的位置 打开

>vi +9 文件位置

struct timespec {

  __kernel_time_t tv_sec;/*seconds*/当前时间到1970.1.1 0:0:0秒数

  long tv_nsec;/*nanoseconds*/纳秒

};

 

3、stat函数介绍2与stat命令

》stat函数参数

  pathname 文件名

  struct stat *buf 传出参数,定义struct stat sb;&sb

  返回值:成功返回0,失败返回-1

>touch stat.c

>vi stat.c

 1 #include<stdio.h>
 2 #include<sys/types.h>
 3 #include<sys/stat.h>
 4 #include<unistd.h>
 5 
 6 int main(int argc, char* argv[])
 7 {
 8     if(argc != 2)
 9     {
10         printf("./a.out filename\n");
11         return -1;
12     }
13     struct stat sb;
14     stat(argv[1],&sb);
15     return 0;
16 }

保存退出

>make

>gdb stat

(gdb)b main

(gdb)run hello

(gdb)n

回车直到return 0;

(gdb)p sb

 输出了结构体sb的信息

(gdb)p/o sb.st_mode

p/o 变量名:可以8进制查看某变量

 

》stat命令

>stat stat.c

还可以输出文件最近访问的时间(文件最新被读的时间)、最近更改的时间(内容更改)、最近改动的时间(大小、权限、硬链接等属性更改)

扩展:重定向可以只更改最近更改时间和最近改动时间,而不会更改文件最近访问时间

如:echo "hello world" >> hello;然后stat hello

 

4、实现ls -l命令

需要使用struct passwd* getpwuid(uid_t uid);获得用户名,需要传入uid

1 struct passwd{
2     char *pw_name;/*username*/用户名
3     char *pw_passwd;/*user password*/
4     uid_t pw_uid;/*user ID*/
5     gid_t pw_gid;/*group ID*/
6     char *pw_gecos;/*user information*/
7     char *pw_dir;/*home directory*/
8     char *pw_shell;/*shell program*/
9 };

struct group *getgrgid(gid_t gid);获得组名

1 struct group{
2     char *gr_name;/*group name*/组名
3     char *gr_passwd;/*group password*/
4     gid_t gr_gid;/*group ID*/
5     char **gr_mem;/*NULL-terminated array of pointers to names of group members*/
6 };

获得本地时间 struct tm *localtime(const time_t *timep); 传入参数timep对应stat函数得到的结构体的秒数(time_t类型),返回tm结构体

 1 struct tm{
 2     int tm_sec;/*Seconds(0-60)*/秒
 3     int tm_min;/*Minutes(0-59)*/分钟
 4     int tm_hour;/*Hours(0-23)*/小时
 5     int tm_mday;/*Day of the month(1-31)*/天
 6     int tm_mon;/*Month(0-11)*/月,需要+1
 7     int tm_year;/*Year-1900*/年,需要+1900
 8     int tm_wday;/*Day of the week(0-6,Sunday=0)*/
 9     int tm_yday;/*Day in the year(0-365,1 Jan = 0)*/
10     int tm_isdst;/*Daylight saving time*/
11 };

>touch ls_l.c

>vi ls_l.c

 1 //-rwrwxr-x 2 wang wang 22 6月 30 10:19 hello
 2 #include<stdio.h>
 3 #include<sys/types.h>
 4 #include<sys/stat.h>
 5 #include<unistd.h>
 6 #include<fcntl.h>
 7 #include<string.h>
 8 #include<time.h>
 9 #include<pwd.h>
10 #include<grp.h>
11 
12 int main(int argc, char* argv[])
13 {
14     if(argc != 2)
15     {
16         printf("./a.out filename\n");
17         return -1;
18     }
19     //调用stat,得到文件属性信息
20     struct stat sb;
21     stat(argv[1],&sb);
22     //解析属性信息,st_mode,uid,gid,time
23     //st_mode
24     char stmode[11] = {0};
25     memset(stmode,'-',sizeof(stmode)-1);
26     if(S_ISREG(sb.st_mode)) stmode[0]='-';//普通文件
27     if(S_ISDIR(sb.st_mode)) stmode[0]='d';
28     if(S_ISCHR(sb.st_mode)) stmode[0]='c';
29     if(S_ISBLK(sb.st_mode)) stmode[0]='b';
30     if(S_ISFIFO(sb.st_mode)) stmode[0]='p';
31     if(S_ISLNK(sb.st_mode)) stmode[0]='l';
32     if(S_ISSOCK(sb.st_mode)) stmode[0]='s';
33     
34     //解析权限
35     if(sb.st_mode & S_IRUSR) stmode[1]='r';
36     if(sb.st_mode & S_IWUSR) stmode[2]='w';
37     if(sb.st_mode & S_IXUSR) stmode[3]='x';
38     
39     if(sb.st_mode & S_IRGRP) stmode[4]='r';
40     if(sb.st_mode & S_IWGRP) stmode[5]='w';
41     if(sb.st_mode & S_IXGRP) stmode[6]='x';
42 
43     if(sb.st_mode & S_IROTH) stmode[7]='r';
44     if(sb.st_mode & S_IWOTH) stmode[8]='w';
45     if(sb.st_mode & S_IXOTH) stmode[9]='x';
46 
47     //分析用户名,组名可以通过函数获得getpwuid,getgrgid
48     //时间获取
49     struct tm *filetm = localtime(&sb.st_atim.tv_sec);
50     char timebuf[20]={0};
51     sprintf(timebuf,"%d月   %d %02d:%02d",filetm->tm_mon+1,filetm->tm_mday,
52         filetm->tm_hour,filetm->tm_min);
53     
54     printf("%s %ld %s %s %ld %s %s\n",stmode,sb.st_nlink,getpwuid(sb.st_uid)->pw_name,getgrgid(sb.st_gid)->gr_name,sb.st_size,timebuf,argv[1]);
55     printf("-rwrwxr-x 2 wang wang 22 6月 30 10:19 hello\n");
56     return 0;
57 }

>make

>ls -lrt

>./ls_l ls_l.c

结果与ls -l ls_l.c进行对比

 

5、stat与lstat的区别

>ln -s hello hello.soft

>ls -lrt

>./ls_l hello.soft

问题:stat编写的ls_l函数在查看软链接时,对软件大小出现错误(显示的是原来文件的大小),穿透功能。

解决:更改为lstat函数

>make

>ls -lrt

>./ls_l hello.soft

注意:stat与lstat的区别:stat碰到链接,会追溯到源文件,穿透!!!lstat并不会穿透。

 

6、access与truncate

》access判断文件的权限是否存在

man 2 access

int access(const char *pathname, int mode);

  pathname 文件

  mode

    R_OK

    W_OK

    X_OK

    F_OK

  返回值:如果有权限或者文件存在,对应返回0,失败返回-1,设置errno

>touch access.c

>vi access.c

 1 #include<stdio.h>
 2 #include<unistd.h>
 3 
 4 int main(int argc, char* argv[])
 5 {
 6     if(argc != 2)
 7     {
 8         printf("./a.out filename\n");
 9         return -1;
10     }
11     
12     if(access(argv[1],R_OK)==0)    printf("%s read ok!\n",argv[1]);
13     if(access(argv[1],W_OK)==0)    printf("%s write ok!\n",argv[1]);
14     if(access(argv[1],X_OK)==0)    printf("%s exe ok!\n",argv[1]);
15     if(access(argv[1],F_OK)==0)    printf("%s file exit!\n",argv[1]);
16     
17     return 0;
18 }

>gcc access.c

>./a.out hello

>sudo ./a.out hello

注意:结果不同,说明针对使用用户不同,看到的结果不一样!

 

》truncate截断文件

man 2 truncate

int truncate(const char *path, off_t length);

  path 文件名

  length 长度,如果大于源文件,直接拓展,如果小于源文件,截断

  返回值:成功返回0,失败返回-1,设置errno

>touch truncate.c

>vi truncate.c

 1 #include<stdio.h>
 2 #include<unistd.h>
 3 #include<sys/types.h>
 4 
 5 int main()
 6 {
 7     truncate("hello",1024);
 8     
 9     return 0;
10 }

>make

>./truncate

>ls -lrt

 

7、readlink、unlink

》link——创建硬链接

man 2 link

int link(const char *oldpath, const char *newpath);

  oldpath 源文件

  newpath 硬链接文件

  返回值:成功返回0,失败返回-1,设置errno

>touch link_symlink.c

>vi link_symlink.c

 1 #include<stdio.h>
 2 #include<unistd.h>
 3 #include<sys/types.h>
 4 
 5 int main()
 6 {
 7     link("hello", "hello.hard1");
 8     
 9     return 0;
10 }

>make

>./link_symlink

>ls -lrt

 

》symlink——创建软链接

man 2 symlink

int symlink(const char *target, const char *linkpath);

  target 源文件

  linkpath 软链接文件

  返回值:成功返回0,失败返回-1,设置errno

>touch link_symlink.c

>vi link_symlink.c

 1 #include<stdio.h>
 2 #include<unistd.h>
 3 #include<sys/types.h>
 4 
 5 int main()
 6 {
 7     //link("hello", "hello.hard1");
 8     link("hello", "hello.soft1");
 9     
10     return 0;
11 }

>make

>./link_symlink

>ls -lrt

 

》readlink——读取符号(软)链接本身内容,得到链接指向的文件名

man 2 readlink

ssize_t readlink(const char *path, char *buf, size_t bufsiz);

  pathname 链接名

  buf 缓冲区

  bufsiz 缓冲区大小

  返回值:成功返回buf填充的大小,失败返回-1,设置errno

>touch readlink_unlink.c

>vi readlink_unlink.c

 1 #include<stdio.h>
 2 #include<unistd.h>
 3 
 4 int main()
 5 {
 6     char buf[32]={0};
    //readlink("hello.hard1", buf, sizeof(buf));//硬链接不行
7 readlink("hello.soft1", buf, sizeof(buf)); 8 printf("buf is %s\n", buf); 9 return 0; 10 }

>make

>./readlink_unlink

 

》unlink——删除符号(软)链接或者是硬链接计数

man 2 unlink

int unlink(const char *pathname);

  pathname 对应的链接名字,文件也可以

  返回值:成功返回0,失败返回-1,设置errno

>touch readlink_unlink.c

>vi readlink_unlink.c

 1 #include<stdio.h>
 2 #include<unistd.h>
 3 
 4 int main()
 5 {
 6     char buf[32]={0};
 7     //readlink("hello.hard1", buf, sizeof(buf));//硬链接不行
 8     readlink("hello.soft1", buf, sizeof(buf));
 9     printf("buf is %s\n", buf);
10     
11     unlink("hello.soft1");
12     unlink("hello.hard1");
13     return 0;
14 }

>make

>./readlink_unlink

 

8、unlink补充

如果文件后边需要用到,unlink先不删除,但是会写成功,进程结束后删除!

>touch unlink.c

>vi unlink.c

 1 #include<stdio.h>
 2 #include<sys/types.h>
 3 #include<sys/stat.h>
 4 #include<fcntl.h>
 5 #include<unistd.h>
 6 #include<string.h>
 7 
 8 int main(int argc, char* argv[])
 9 {
10     int fd = open("world",O_WRONLY|O_CREAT,0666);
11     
12     unlink("world");
13 
14     int ret = write(fd,"hello", 5);
15     if(ret > 0){
16         printf("write ok!%d\n", ret);
17     }
18     if(ret < 0){
19         perror("write err");
20     }
21 
22     close(fd);
23     return 0;
24 }

>make

>./unlink

 

9、chown与rename

》chown——改变用户和组

man 2 chown

int chown(const char *pathname, uid_t owner, gid_t group);

  pathname 文件名

  owner 用户ID,/etc/passwd

  group 组ID,/etc/group

》rename——重命名文件

man 2 rename

int rename(const char *oldpath, const char *newpath);

  oldpath 旧文件

  newpath 新文件

  返回值:成功返回0,失败返回-1,设置errno

>touch rename.c

>vi rename.c

>mkdir aaa

 1 #include<stdio.h>
 2 
 3 int main()
 4 {
 5     //改文件名
 6     //rename("a.out", "a.new");
 7     //改目录
 8     rename("aaa","bbb")
 9     
10     return 0;
11 }

>make

>./rename

 

目录相关函数

10、chdir、getcwd切换目录和获得工作路径

 》getcwd——获得当前工作路径

man 2 getcwd

char *getcwd(char *buf, size_t size);

  buf 传出参数,路径

  size 缓冲区大小

  返回值:成功返回路径的指针,失败返回NULL

 》chdir——改变工作路径,注意属于进程独有的!

man 2 chdir

int chdir(const char *path);

  path 对应的目标工作路径

  返回值:成功返回0,失败返回-1,设置errno

>touch cwd_chdir.c

>vi cwd_chdir.c

 1 #include<stdio.h>
 2 #include<unistd.h>
 3 #include<sys/types.h>
 4 #include<sys/stat.h>
 5 #include<fcntl.h>
 6 
 7 int main(int argc, char* argv[])
 8 {
 9     //先切换工作目录
10     chdir("bbb");
11     
12     //留下点痕迹
13     int fd = open("temp",O_WRONLY|O_CREAT,0666);
14     write(fd, "daociyiyou", 10);
15     close(fd);
16     //显示当前工作目录
17     char buf[256];
18     getcwd(buf, sizeof(buf));
19     
20     printf("buf is [%s]\n", buf);
21     close(fd);
22     return 0;
23 }

>make

>./cwd_chdir

 

11、mkdir创建目录

》mkdir——创建目录

man 2 mkdir

int mkdir(const char *pathname, mode_t mode);

  pathname 路径

  mode mode & ~umask(0777)注意:权限问题,如果目录没有可执行权限,不可进入

  返回值:成功返回0,失败返回-1,设置errno

>touch mkdir.c

>vi mkdir.c

 1 #include<stdio.h>
 2 #include<sys/types.h>
 3 #include<sys/stat.h>
 4 
 5 
 6 int main(int argc, char* argv[])
 7 {
 8     if(argc != 2)
 9     {
10         printf("./a.out filename\n");
11         return -1;
12     }
13     
14     mkdir(argv[1],0777);
15     return 0;
16 }

>make

>./mkdir

 

12、读目录相关函数介绍

 》rmdir——删除空目录

man 2 rmdir

int rmdir(const char *pathname);

 

需求:统计一下指定目录下普通文件的个数,要求子目录递归

(shell计数:find ./ -type f | wc -l)

 

》opendir——打开目录

man 2 opendir

DIR *opendir(const char *name);

  name 打开的目录

  返回值:成功返回DIR*的指针,指向目录项的信息,失败返回NULL

》readdir——读目录

man 2 readdir

struct dirent *readdir(DIR *dirp);

  dirp 传入参数,opendir返回的指针

  返回值:成功为读到目录项的内容,读到末尾或者有错误返回NULL

》closedir——关闭目录

man 2 closedir

int closedir(DIR *dirp);

  dirp opendir得到的指针

 

13、递归子目录统计普通文件个数

>touch filecount.c

>vi filecount.c

 1 #include<stdio.h>
 2 #include<unistd.h>
 3 #include<dirent.h>
 4 #include<sys/types.h>
 5 #include<string.h>
 6 
 7 int count = 0;//定义一个全局的计数
 8 
 9 int DirCount(char *dirname)
10 {
11     printf("%s\n",dirname);
12     //打开目录
13     DIR *dirp = opendir(dirname);
14     if(dirp == NULL){
15         perror("opendir err");
16         return -1;
17     }
18     //循环读目录,如果是普通文件,count++,如果是目录,继续调用DirCount
19     struct dirent *dentp = NULL;
20     while((dentp = readdir(dirp))!= NULL){//如果为NULL,代表读到目录末尾
21         //printf("dirname:%s,dtype:%d\n",dentp->d_name,dentp->d_type);
22         if(dentp->d_type == DT_DIR){//如果是目录
23             if(strcmp(".",dentp->d_name)==0 || strcmp("..",dentp->d_name)==0){
24                 continue;
25             }
26             //注意进程的工作路径,不能直接打开子目录
27             //使用dirname拼接下一级子目录
28             char newdirname[256]={0};
29             sprintf(newdirname,"%s/%s",dirname,dentp->d_name);
30             DirCount(newdirname);
31         }
32         if(dentp->d_type == DT_REG){
33             //普通文件,开始计数
34             count++;
35             printf("dname:%s\n",dentp->d_name);
36         }
37     }
38     //关闭目录
39     closedir(dirp);
40     return 0;
41 }
42 
43 int main(int argc, char* argv[])
44 {
45     if(argc != 2)
46     {
47         printf("./a.out dirname\n");
48         return -1;
49     }
50     
51     DirCount(argv[1]);
52     printf("count=%d\n",count);
53     //打开目录
54     struct DIR *dirp = opendir(argv[1]);
55     //循环读目录,判断如果是普通文件,计数++
56     readdir();
57     //关闭目录
58     
59     return 0;
60 }

>make

>./filecount ./

>find ./ -type f | wc -l

(采用系统shell计数对比下二者计数是否相同)

 

14、errno说明

按全局变量理解,存储是错误信息

》查看vi /usr/include/asm-generic/errno.h

》查看vi /usr/include/asm-generic/errno-base.h

errno输出函数:可以用strerror打印出错误信息

man 2 strerror

char * strerror(int errnum);

 

15、dup2和dup说明

 》dup2——重定向

man 2 dup2

int dup2(int oldfd, int newfd);

  关闭newfd对应的文件描述符,将newfd重新指向为oldfd对应的文件

》dup——复制文件描述符

int dup(int oldfd);

  新返回一个文件描述符指向oldfd对应的文件

 

16、dup2和dup的使用

需求:在代码中执行2次print("hello world\n");前一次输出到hello文件中,后一次输出到屏幕上。

>touch dup2du.c

>vi dup2du.c

 1 #include<stdio.h>
 2 #include<unistd.h>
 3 #include<sys/types.h>
 4 #include<sys/stat.h>
 5 #include<fcntl.h>
 6 
 7 int main()
 8 {
 9     //先备份现场
10     int outfd = dup(1);
11     //然后做重定向
12     int fd = open("world",O_WRONLY|O_CREAT,0666);
13     dup2(fd,1);//将标准输出重定向到fd对应的文件
14     printf("hello world\n");
15     
16     //需要来一次刷新
17     fflush(stdout);
18     
19     //需要恢复1重新对应标准输出
20     dup2(outfd,1);
21     
22     
23     printf("hello world\n");
24     close(fd);
25     return 0;
26 }

>make

>./dup2du

拓:fcntl也可以复制文件描述符。

 

 

在学习Linux命令基础总结了笔记,并分享出来。有问题请及时联系博主:Alliswell_WP,转载请注明出处。

posted on 2020-06-29 19:04  Alliswell_WP  阅读(359)  评论(0编辑  收藏  举报

导航