二、与文件属性相关的操作函数
    acess判断文件的权限(或文件是否存在) chmod修改文件的权限 chown修改文件的所有者或所在组 truncate用于缩减或扩展某个文件的大小

    1. /*
    2. #include <unistd.h>
    3. int access(const char *pathname, int mode);
    4. 作用:判断某个文件是否有某个权限(通过当前进程是否能访问某个文件来判断)
    5. 或者判断文件是否存在
    6. 参数:
    7. 文件名
    8. mode:待判断的当前进程对这个文件权限
    9. F_OK(用于检查文件是否存在)
    10. R_OK(是否可读) W_OK(是否可写) X_OK(是否可执行)
    11. 返回值:
    12. 成功返回0 失败返回-1
    13. */
    14. #include <unistd.h>
    15. #include <stdio.h> //perror
    16. int main()
    17. {
    18. int ret = access("a.txt", F_OK); //判断 a.txt是否存在
    19. if (ret == -1)
    20. {
    21. perror("file not exist");
    22. }
    23. return 0;
    24. }
    25. // gcc access.c -o access
    26. /*
    27. #include <sys/stat.h>
    28. int chmod(const char *pathname, mode_t mode);
    29. 用于修改当前进程对pathname文件的权限
    30. 参数:
    31. 文件名
    32. mode_t mode 需要修改的权限 给定相应的宏 让这个进程拥有对文件的处理的相应权限
    33. 也可以像例子中那样直接给个八进制数0775
    34. S_ISUID (04000 八进制) set-user-ID (set process effective user ID on execve(2))
    35. S_ISGID (02000) set-group-ID (set process effective group ID on execve(2);
    36. mandatory locking, as described in fcntl(2); take a new
    37. file's group from parent directory, as described in chown(2)
    38. and mkdir(2))
    39. S_ISVTX (01000) sticky bit (restricted deletion flag, as described in
    40. unlink(2))
    41. S_IRUSR (00400) read by owner
    42. S_IWUSR (00200) write by owner
    43. S_IXUSR (00100) execute/search by owner ("search" applies for directories,
    44. and means that entries within the directory can be accessed)
    45. S_IRGRP (00040) read by group
    46. S_IWGRP (00020) write by group
    47. S_IXGRP (00010) execute/search by group
    48. S_IROTH (00004) read by others
    49. 返回值:
    50. 成功返回0 失败返回-1
    51. */
    52. #include <sys/stat.h>
    53. int main()
    54. {
    55. //-rw-rw-r-- 664 原a.txt权限--->-rwxrwxr-x 775
    56. int ret = chmod("a.txt", 0775);
    57. if (ret == -1)
    58. {
    59. return -1; //fail
    60. }
    61. return 0;
    62. }
    63. /*
    64. #include <unistd.h>
    65. int chown(const char *pathname, uid_t owner, gid_t group);
    66. uid_t owner 待改为哪个用户id所有这个文件
    67. gid_t group 待改为哪个组id所有这个文件
    68. vim /etc/passwd 当中写了所有的用户
    69. wen:x:1000(用户id):1000(组id):wen,,,:/home/wen:/bin/bash
    70. vim /etc/group 当中写了所有的组
    71. wen:x:1000(组id):
    72. sudo useradd 用户名 来添加用户
    73. id 用户名 来查看用户的uid与gid uid=1000(wen) gid=1000(wen) 组=1000(wen 组名)
    74. 要想通过chown修改文件的所有者和所有组 在编译完后
    75. 需要 sudo ./可执行文件 才有权限去修改!!!!!!!!!
    76. */
    77. #include <sys/stat.h>
    78. int main()
    79. {
    80. int ret = chown("a.txt", 1000, 1000);
    81. if (ret == -1)
    82. {
    83. return -1; //fail
    84. }
    85. return 0;
    86. }
    1. /*
    2. #include <unistd.h>
    3. #include <sys/types.h>
    4. int truncate(const char *path, off_t length);
    5. 用于缩减或拓展文件的大小
    6. 参数:
    7. 文件路径
    8. length 文件最终的大小(文件经过这个函数后就直接变这么大了) 如需清空文件,length设置为0
    9. 这个大小大于当前大小 则在文件尾添加一系列空字节 小于则将超出部分删掉
    10. 返回值:
    11. 成功返回0 失败返回-1并设置errno
    12. 1 EACCESS 参数path所指定的文件无法存取
    13. 2 EROFS 欲写入的文件存在于只读文件系统内
    14. 3 EFAULT 参数path指针超出可存取空间
    15. 4 EINVAL 参数path包含不合法字符
    16. 5 ENAMETOOLONG 参数path太长
    17. 6 ENOTDIR 参数path路径并非一目录
    18. 7 EISDIR 参数path指向一目录
    19. 8 ETXTBUSY 参数path所指的文件为共享程序,而且正被执行中
    20. 9 ELOOP 参数path 有过多符号连接问题。
    21. 10 EIO I/O存取错误
    22. */
    23. #include <unistd.h>
    24. #include <sys/types.h>
    25. int main()
    26. {
    27. int ret = truncate("a.txt", 20); //将a.txt变为20个字节
    28. if (ret == -1)
    29. {
    30. return -1; //fail
    31. }
    32. return 0;
    33. }

    三、目录操作函数

    1. /*
    2. #include <sys/stat.h>
    3. #include <sys/types.h>
    4. int mkdir(const char *pathname, mode_t mode);
    5. man 2 mkdir man 1 mkdir为linux系统命令说明
    6. 创建一个目录
    7. 参数:
    8. pathname 想要创建的目录的路径
    9. mode mode_t 8进制数代表这个目录的权限 具体可看use_chmod.c中对mode的详细解释 这里跟那边是一样的
    10. 返回值:
    11. 成功返回0 失败返回-1
    12. */
    13. #include <sys/stat.h>
    14. #include <sys/types.h>
    15. int main()
    16. {
    17. int ret = mkdir("aaa", 0777); //在当前路径下再生成 /aaa 相当于生成./aaa的路径
    18. //虽然指定777但实际生成的为775(因为与umask掩码相关 具体可看use_open.c的说明)
    19. //注意目录只有拥有可执行权限 我们才能进入这个目录中 比如用户对这个文件(目录)拥有可执行文件 用户才能进入到这个路径下
    20. if (ret == -1)
    21. {
    22. return -1; //fail
    23. }
    24. return 0;
    25. }
    1. #include <unistd.h>
    2. int rmdir(const char *pathname);//用于删除空目录 目录内有内容就删不掉了
    3. #include <stdio.h>
    4. int rename(const char *oldpath, const char *newpath);//将oldpath用newpath字符串替换
    5. rename(“aaa”,”bbb”);//将aaa用bbb替代
    1. /*
    2. #include <unistd.h>
    3. int chdir(const char *path);//cd
    4. 改变(当前进程所处)当前的工作目录为path(绝对路径)
    5. 成功返回0,失败返回-1
    6. 进程的初始工作目录为 你在哪个路径下将它启动 它的初始工作路径就在那
    7. #include <unistd.h>
    8. char *getcwd(char *buf, size_t size);//pwd
    9. 获得当前的工作的绝对路径(当前进程所处) 用buf传出路径字符串
    10. 返回的是buf的首地址,为了方便直接输出buf字符串
    11. */
    12. #include <unistd.h>
    13. #include <sys/types.h> //flags宏在这个头文件
    14. #include <sys/stat.h> //flags宏在这个头文件里也有
    15. #include <fcntl.h> //函数声明在这个头文件中
    16. #include <stdio.h> //perror
    17. #include <unistd.h> //close
    18. //gcc use_cd_pwd.c -o cd
    19. int main()
    20. {
    21. //获取当前工作目录
    22. char buf[128];
    23. printf("当前的工作路径是:%s\n", getcwd(buf, sizeof(buf))); // /home/wen/lesson11
    24. //修改工作目录
    25. int ret = chdir("/home/wen/lesson10");
    26. if (ret == -1)
    27. {
    28. perror("cd fail");
    29. return -1; //fail
    30. }
    31. //创建新的文件
    32. int fd = open("chdir210.txt", O_CREAT | O_RDWR, 0664); //生成了/home/wen/lesson10/chdir210.txt
    33. if (ret == -1)
    34. {
    35. perror("create fail");
    36. return -1; //fail
    37. }
    38. close(fd);
    39. char buf2[128];
    40. printf("当前的工作路径是:%s\n", getcwd(buf2, sizeof(buf2))); // /home/wen/lesson10
    41. return 0;
    42. }

    四、目录遍历

    1. /*
    2. 读取目录下普通文件的个数
    3. man 3 标准c
    4. #include <sys/types.h>
    5. #include <dirent.h>
    6. DIR *opendir(const char *name);
    7. 获取path子目录下的所由文件和目录的列表,如果path是个文件(或其他失败场景)
    8. 则返回值为NULL
    9. 正常返回目录流的第一个进入点
    10. typedef struct __dirstream DIR;
    11. __dirstream为目录流类型 其内容对用户不开放看不到其中内容
    12. #include <dirent.h>
    13. struct dirent *readdir(DIR *dirp);
    14. dirp 为 opendir返回的结果
    15. 读取目录中的数据 每调用一次DIR内部指针就自动指向drip中的下个实体
    16. 返回参数dirp 目录流的下个目录进入点 信息保存在结构体struct dirent *中
    17. struct dirent {
    18. ino_t d_ino; 此目录进入点的inode节点编号 Inode number
    19. off_t d_off; 目录文件开头到此目录进入节点的偏移 Not an offset; see below
    20. unsigned short d_reclen; d_name文件名的实际长度(占了[256]中的几个 到底文件名为多少个字符) 不包含NULL字符 Length of this record
    21. unsigned char d_type; d_name文件的类型 Type of file; not supported
    22. by all filesystem types
    23. char d_name[256]; 文件名 Null-terminated filename
    24. };
    25. 其中d_name文件的类型d_type有如下几种
    26. DT_BLK 块设备
    27. DT_CHR 字符设备
    28. DT_DIR 目录
    29. DT_LNK 软连接
    30. DT_FIFO 管道
    31. DT_REG 普通文件
    32. DT_SOCK 套接字
    33. DT_UNKNOWN 未知
    34. 当读取到末尾(不会设置errno或失败(会设置errno) 返回NULL
    35. #include <sys/types.h>
    36. #include <dirent.h>
    37. int closedir(DIR *dirp);
    38. 关闭目录
    39. */
    40. #include <sys/types.h>
    41. #include <dirent.h>
    42. #include <stdio.h>
    43. #include <string.h>
    44. //读取某个目录下(嵌套直到最深)所有普通文件的个数
    45. // ./count.out 目录路径
    46. void getFileNum(const char *path, int *res);
    47. int main(int argc, char *argv[])
    48. {
    49. if (argc < 2)
    50. {
    51. //一般一定会有一个参数 是可执行文件的路径
    52. //再加上跟上的文件名 一共至少为两个参数 模拟才会生效
    53. //所以这里判断一下参数个数小于2个就return
    54. printf("%s filename\n", argv[0]); //argv[0]为执行文件的路径
    55. return -1;
    56. }
    57. int ret = 0; //普通文件的个数
    58. getFileNum(argv[1], &ret);
    59. printf("共有普通文件%d个\n", ret);
    60. return 0;
    61. }
    62. //递归 遍历所有子目录(含子目录的子目录...) 统计其中普通文件的个数
    63. void getFileNum(const char *path, int *res)
    64. {
    65. // 打开目录
    66. DIR *dir = opendir(path);
    67. if (dir == NULL)
    68. {
    69. perror("dir is null");
    70. return;
    71. }
    72. struct dirent *ptr;
    73. //循环读取子目录
    74. while ((ptr = readdir(dir)) != NULL) //只要没读到末尾(不为NULL) 或出错 就一直循环下去 每调用一次DIR内部指针就自动指向drip中的下个实体
    75. {
    76. /* code */
    77. //获取名称 不能将./ ../算入子目录中
    78. char *dname = ptr->d_name;
    79. if (strcmp(dname, ".") == 0 || strcmp(dname, "..") == 0) //过滤. .. strcmp==0说明两个字符串相同
    80. {
    81. continue;
    82. }
    83. //判断是否是普通文件res++ 还是目录(递归调用)
    84. if (ptr->d_type == DT_DIR) //是目录 注意dname仅仅是目录的名字而非完整的路径
    85. {
    86. //opendir的path要求是完整路径所以递归调用时传的path也要是完整路径
    87. char newpath[256];
    88. sprintf(newpath, "%s/%s", path, dname); //拼接得到dname对应文件的完整路径 要s/s
    89. getFileNum(newpath, res); //递归调用 遍历子目录内的文件
    90. }
    91. else if (ptr->d_type == DT_REG) //是普通文件
    92. {
    93. (*res)++; //要括号*优先级不高
    94. }
    95. }
    96. closedir(dir);
    97. return;
    98. }

    五、文件描述符重定向

    1. /*
    2. #include <unistd.h>
    3. int dup(int oldfd);
    4. 系统调用 传入被拷贝的描述符 用未被使用的数值最小的文件描述符作为新的描述符
    5. 注意拷贝的是文件描述符指向的FILE结构体的内容 而非真的拷贝文件!!!! 新的描述符和老的描述符指向的是同一个文件!!!
    6. 如果拷贝成功返回这个新的的描述符 失败返回-1并设置errno
    7. */
    8. #include <unistd.h>
    9. #include <stdio.h>
    10. #include <fcntl.h>
    11. #include <sys/types.h>
    12. #include <sys/stat.h>
    13. #include <string.h>
    14. int main()
    15. {
    16. int fd = open("a.txt", O_RDWR | O_CREAT, 0664); //创造新的文件a.txt 因为fd 1,2为流的fd已占用 这个新的fd因该是3
    17. int newfd = dup(fd);
    18. if (newfd == -1)
    19. {
    20. perror("dup fail");
    21. return -1;
    22. }
    23. print("fd:%d newfd:%d \n", fd, newfd); //fd:3 newfd:4
    24. //关闭fd 用newfd向写a.txt写内容 成功 证明dup后 newfd和fd指向的是同一个文件
    25. close(fd);
    26. char *str = "hello";
    27. int ret = write(newfd, str, strlen(str));
    28. if (ret == -1)
    29. {
    30. perror("write fail");
    31. return -1;
    32. }
    33. close(newfd);
    34. return 0;
    35. }
    1. /*
    2. #include <unistd.h>
    3. int dup2(int oldfd, int newfd);//功能和dup类似
    4. 用于重定向文件描述符
    5. oldfd指向a.txt newfd指向b.txt 在调用函数成功后将newfd->b.txt关闭并且再让newfd指向a.txt
    6. oldfd必须是一个有效的 真实指向文件的描述符 如果是个无效的文件描述符 并不会做关闭newfd操作 并且返回-1设置errno
    7. 如果oldfd和newfd值相同 相当于什么都没做 直接返回newfd并且不会做关闭newfd操作
    8. 正常返回的就是newfd
    9. */
    10. #include <unistd.h>
    11. #include <stdio.h>
    12. #include <fcntl.h>
    13. #include <sys/types.h>
    14. #include <sys/stat.h>
    15. #include <string.h>
    16. int main()
    17. {
    18. int oldfd = open("a.txt", O_RDWR | O_CREAT, 0664); //创造新的文件a.txt 因为fd 1,2为流的fd已占用 这个新的fd因该是3
    19. if (oldfd == -1)
    20. {
    21. perror("open fail");
    22. return -1;
    23. }
    24. int newfd = open("b.txt", O_RDWR | O_CREAT, 0664); //创造新的文件a.txt 因为fd 1,2为流的fd已占用 这个新的fd因该是3
    25. if (newfd == -1)
    26. {
    27. perror("open fail");
    28. return -1;
    29. }
    30. print("oldfd:%d newfd:%d \n", oldfd, newfd);
    31. int retfd = dup2(oldfd, newfd);
    32. print("oldfd:%d newfd:%d \n", oldfd, newfd);
    33. if (retfd == -1)
    34. {
    35. perror("dup2 fail");
    36. return -1;
    37. }
    38. //oldfd与newfd在dup2后指向的都是a.txt 下面用newfd来验证一下
    39. close(oldfd);
    40. char *str = "I'm newfd";
    41. int ret = write(newfd, str, strlen(str));
    42. if (ret == -1)
    43. {
    44. perror("write fail");
    45. return -1;
    46. }
    47. close(newfd);
    48. return 0;
    49. }

    六、fcntl复制文件描述符 设置/获取文件的状态标志

    1. /*
    2. fcntl复制文件描述符 设置/获取文件的状态标志
    3. #include <unistd.h>
    4. #include <fcntl.h>
    5. int fcntl(int fd, int cmd, ... arg );//...表示可变参数 可写也可不写
    6. fd 被操作的文件描述符
    7. cmd 对文件描述符做什么操作
    8. 对fd文件做cmd操作
    9. cmd:
    10. F_DUPFD 复制文件描述符
    11. F_GETFL 获取指定文件描述符的文件状态flag
    12. 文件状态flag在学习open的时候有提到
    13. flags:对文件的操作权限设置 以及其他的一些设置
    14. O_RDONLY,O_WRONLY,O_RDWR 三个宏分别对应只读,只写,读写。
    15. 这三个设置是互斥的,1次open只能选1个必选项。
    16. O_APPEND 不覆盖在尾部继续追加
    17. O_CREAT 如果文件不存在则创建新文件
    18. 等的可选项
    19. 在有多个选项时通过|按位或连接如O_RDWR | O_CREAT
    20. flags是int型占4个字节 为32位其中每一位就是一个标志位每一位代表一种情况(1种标记 可读 可写 可读写 创建新文件)
    21. F_SETFL 设置指定文件描述符的文件状态flag(O_DSYNC and O_SYNC文件状态无法通过setfl改变)
    22. O_RDONLY,O_WRONLY,O_RDWR 三个宏分别对应只读,只写,读写。
    23. 这三个设置是互斥的,1次open只能选1个必选项,
    24. 这三个必选项是无法修改的即无法通过F_SETFL设置!!!
    25. 并且与文件创建有关的状态 O_CREAT O_EXCL O_NOCTTY O_TRUNC都是无法设置的!!!
    26. 可选项 O_APPEND 不覆盖在尾部继续追加 O_NONBLOCK 非阻塞 等是可设置的
    27. 这里的阻塞与非阻塞描述的是函数调用的行为,
    28. 所谓阻塞方式block,就是进程或是线程执行到这些函数时必须等待某个事件的发生,如果事件没有发生,进程或线程就被阻塞,函数不能立即返回。
    29. 所谓非阻塞方式non-block,就是进程或线程执行此函数时不必非要等待事件的发生,一旦执行肯定返回,以返回值的不同来反映函数的执行情况,
    30. 如果事件发生则与阻塞方式相同,若事件没有发生则返回一个代码来告知事件未发生,而进程或线程继续执行,所以效率较高
    31. 复制传入的fd,并用未被使用的数值最小的文件描述符作为新的描述符 新描述符和fd指向的是同一文件
    32. 如果复制成功 返回值就是新的描述符
    33. */
    34. #include <unistd.h>
    35. #include <fcntl.h>
    36. #include <stdio.h>
    37. int main()
    38. {
    39. //1复制文件描述符
    40. //int fd = open("1.txt", O_RDONLY);
    41. // int ret = fcntl(fd, F_DUPFD); //返回复制的那个新的描述符 这个新的描述符和fd都是指向1.txt的
    42. //2修改和获取文件状态flag
    43. int fd = open("1.txt", O_RDWR);
    44. if (fd == -1)
    45. {
    46. perror("open fail");
    47. return -1;
    48. }
    49. //当前是不能写文件的 下面先修改文件状态给flag加入O_APPEND 之后就能在文件尾添加
    50. int flag = fcntl(fd, F_GETFL); //先获取当前文件描述符的状态
    51. //如果直接给fcntl(fd, F_SETFL,O_APPEND); 那么之前的状态O_RDWR会被直接覆盖 当前线程对此文件不再可读写
    52. //flag | O_APPEND 在原基础上追加O_APPEND
    53. int ret = fcntl(fd, F_SETFL, flag | O_APPEND);
    54. //F_SETFL 失败返回-1 成功返回0
    55. close(fd);
    56. return 0;
    57. }