• 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
    ", 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
    ");
    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
    ");
    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
    ",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
    ");
    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
    ");
     9         return -1;
    10     }
    11     
    12     if(access(argv[1],R_OK)==0)    printf("%s read ok!
    ",argv[1]);
    13     if(access(argv[1],W_OK)==0)    printf("%s write ok!
    ",argv[1]);
    14     if(access(argv[1],X_OK)==0)    printf("%s exe ok!
    ",argv[1]);
    15     if(access(argv[1],F_OK)==0)    printf("%s file exit!
    ",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 ", 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
    ", 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
    ", 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]
    ", 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
    ");
    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
    ",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
    ",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
    ",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
    ");
    48         return -1;
    49     }
    50     
    51     DirCount(argv[1]);
    52     printf("count=%d
    ",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 ");前一次输出到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
    ");
    15     
    16     //需要来一次刷新
    17     fflush(stdout);
    18     
    19     //需要恢复1重新对应标准输出
    20     dup2(outfd,1);
    21     
    22     
    23     printf("hello world
    ");
    24     close(fd);
    25     return 0;
    26 }

    >make

    >./dup2du

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

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

  • 相关阅读:
    2019年10月24日打印个人信息清单
    vsftp安装
    网络连接
    mysql多种方法修改密码----5.6的坑
    openstack-L版安装
    openstack是什么
    kvm快照
    文件操作
    函数
    virt-manage图形界面键盘错位问题
  • 原文地址:https://www.cnblogs.com/Alliswell-WP/p/CPlusPlus_Linux_04.html
Copyright © 2020-2023  润新知