二、与文件属性相关的操作函数
acess判断文件的权限(或文件是否存在) chmod修改文件的权限 chown修改文件的所有者或所在组 truncate用于缩减或扩展某个文件的大小
/*#include <unistd.h>int access(const char *pathname, int mode);作用:判断某个文件是否有某个权限(通过当前进程是否能访问某个文件来判断)或者判断文件是否存在参数:文件名mode:待判断的当前进程对这个文件权限F_OK(用于检查文件是否存在)R_OK(是否可读) W_OK(是否可写) X_OK(是否可执行)返回值:成功返回0 失败返回-1*/#include <unistd.h>#include <stdio.h> //perrorint main(){int ret = access("a.txt", F_OK); //判断 a.txt是否存在if (ret == -1){perror("file not exist");}return 0;}// gcc access.c -o access/*#include <sys/stat.h>int chmod(const char *pathname, mode_t mode);用于修改当前进程对pathname文件的权限参数:文件名mode_t mode 需要修改的权限 给定相应的宏 让这个进程拥有对文件的处理的相应权限也可以像例子中那样直接给个八进制数0775S_ISUID (04000 八进制) set-user-ID (set process effective user ID on execve(2))S_ISGID (02000) set-group-ID (set process effective group ID on execve(2);mandatory locking, as described in fcntl(2); take a newfile's group from parent directory, as described in chown(2)and mkdir(2))S_ISVTX (01000) sticky bit (restricted deletion flag, as described inunlink(2))S_IRUSR (00400) read by ownerS_IWUSR (00200) write by ownerS_IXUSR (00100) execute/search by owner ("search" applies for directories,and means that entries within the directory can be accessed)S_IRGRP (00040) read by groupS_IWGRP (00020) write by groupS_IXGRP (00010) execute/search by groupS_IROTH (00004) read by others返回值:成功返回0 失败返回-1*/#include <sys/stat.h>int main(){//-rw-rw-r-- 664 原a.txt权限--->-rwxrwxr-x 775int ret = chmod("a.txt", 0775);if (ret == -1){return -1; //fail}return 0;}/*#include <unistd.h>int chown(const char *pathname, uid_t owner, gid_t group);uid_t owner 待改为哪个用户id所有这个文件gid_t group 待改为哪个组id所有这个文件vim /etc/passwd 当中写了所有的用户wen:x:1000(用户id):1000(组id):wen,,,:/home/wen:/bin/bashvim /etc/group 当中写了所有的组wen:x:1000(组id):sudo useradd 用户名 来添加用户id 用户名 来查看用户的uid与gid uid=1000(wen) gid=1000(wen) 组=1000(wen 组名)要想通过chown修改文件的所有者和所有组 在编译完后需要 sudo ./可执行文件 才有权限去修改!!!!!!!!!*/#include <sys/stat.h>int main(){int ret = chown("a.txt", 1000, 1000);if (ret == -1){return -1; //fail}return 0;}
/*#include <unistd.h>#include <sys/types.h>int truncate(const char *path, off_t length);用于缩减或拓展文件的大小参数:文件路径length 文件最终的大小(文件经过这个函数后就直接变这么大了) 如需清空文件,length设置为0这个大小大于当前大小 则在文件尾添加一系列空字节 小于则将超出部分删掉返回值:成功返回0 失败返回-1并设置errno1 EACCESS 参数path所指定的文件无法存取2 EROFS 欲写入的文件存在于只读文件系统内3 EFAULT 参数path指针超出可存取空间4 EINVAL 参数path包含不合法字符5 ENAMETOOLONG 参数path太长6 ENOTDIR 参数path路径并非一目录7 EISDIR 参数path指向一目录8 ETXTBUSY 参数path所指的文件为共享程序,而且正被执行中9 ELOOP 参数path 有过多符号连接问题。10 EIO I/O存取错误*/#include <unistd.h>#include <sys/types.h>int main(){int ret = truncate("a.txt", 20); //将a.txt变为20个字节if (ret == -1){return -1; //fail}return 0;}
三、目录操作函数
/*#include <sys/stat.h>#include <sys/types.h>int mkdir(const char *pathname, mode_t mode);man 2 mkdir man 1 mkdir为linux系统命令说明创建一个目录参数:pathname 想要创建的目录的路径mode mode_t 8进制数代表这个目录的权限 具体可看use_chmod.c中对mode的详细解释 这里跟那边是一样的返回值:成功返回0 失败返回-1*/#include <sys/stat.h>#include <sys/types.h>int main(){int ret = mkdir("aaa", 0777); //在当前路径下再生成 /aaa 相当于生成./aaa的路径//虽然指定777但实际生成的为775(因为与umask掩码相关 具体可看use_open.c的说明)//注意目录只有拥有可执行权限 我们才能进入这个目录中 比如用户对这个文件(目录)拥有可执行文件 用户才能进入到这个路径下if (ret == -1){return -1; //fail}return 0;}
#include <unistd.h>int rmdir(const char *pathname);//用于删除空目录 目录内有内容就删不掉了#include <stdio.h>int rename(const char *oldpath, const char *newpath);//将oldpath用newpath字符串替换rename(“aaa”,”bbb”);//将aaa用bbb替代
/*#include <unistd.h>int chdir(const char *path);//cd改变(当前进程所处)当前的工作目录为path(绝对路径)成功返回0,失败返回-1进程的初始工作目录为 你在哪个路径下将它启动 它的初始工作路径就在那#include <unistd.h>char *getcwd(char *buf, size_t size);//pwd获得当前的工作的绝对路径(当前进程所处) 用buf传出路径字符串返回的是buf的首地址,为了方便直接输出buf字符串*/#include <unistd.h>#include <sys/types.h> //flags宏在这个头文件#include <sys/stat.h> //flags宏在这个头文件里也有#include <fcntl.h> //函数声明在这个头文件中#include <stdio.h> //perror#include <unistd.h> //close//gcc use_cd_pwd.c -o cdint main(){//获取当前工作目录char buf[128];printf("当前的工作路径是:%s\n", getcwd(buf, sizeof(buf))); // /home/wen/lesson11//修改工作目录int ret = chdir("/home/wen/lesson10");if (ret == -1){perror("cd fail");return -1; //fail}//创建新的文件int fd = open("chdir210.txt", O_CREAT | O_RDWR, 0664); //生成了/home/wen/lesson10/chdir210.txtif (ret == -1){perror("create fail");return -1; //fail}close(fd);char buf2[128];printf("当前的工作路径是:%s\n", getcwd(buf2, sizeof(buf2))); // /home/wen/lesson10return 0;}
四、目录遍历
/*读取目录下普通文件的个数man 3 标准c#include <sys/types.h>#include <dirent.h>DIR *opendir(const char *name);获取path子目录下的所由文件和目录的列表,如果path是个文件(或其他失败场景)则返回值为NULL正常返回目录流的第一个进入点typedef struct __dirstream DIR;__dirstream为目录流类型 其内容对用户不开放看不到其中内容#include <dirent.h>struct dirent *readdir(DIR *dirp);dirp 为 opendir返回的结果读取目录中的数据 每调用一次DIR内部指针就自动指向drip中的下个实体返回参数dirp 目录流的下个目录进入点 信息保存在结构体struct dirent *中struct dirent {ino_t d_ino; 此目录进入点的inode节点编号 Inode numberoff_t d_off; 目录文件开头到此目录进入节点的偏移 Not an offset; see belowunsigned short d_reclen; d_name文件名的实际长度(占了[256]中的几个 到底文件名为多少个字符) 不包含NULL字符 Length of this recordunsigned char d_type; d_name文件的类型 Type of file; not supportedby all filesystem typeschar d_name[256]; 文件名 Null-terminated filename};其中d_name文件的类型d_type有如下几种DT_BLK 块设备DT_CHR 字符设备DT_DIR 目录DT_LNK 软连接DT_FIFO 管道DT_REG 普通文件DT_SOCK 套接字DT_UNKNOWN 未知当读取到末尾(不会设置errno或失败(会设置errno) 返回NULL#include <sys/types.h>#include <dirent.h>int closedir(DIR *dirp);关闭目录*/#include <sys/types.h>#include <dirent.h>#include <stdio.h>#include <string.h>//读取某个目录下(嵌套直到最深)所有普通文件的个数// ./count.out 目录路径void getFileNum(const char *path, int *res);int main(int argc, char *argv[]){if (argc < 2){//一般一定会有一个参数 是可执行文件的路径//再加上跟上的文件名 一共至少为两个参数 模拟才会生效//所以这里判断一下参数个数小于2个就returnprintf("%s filename\n", argv[0]); //argv[0]为执行文件的路径return -1;}int ret = 0; //普通文件的个数getFileNum(argv[1], &ret);printf("共有普通文件%d个\n", ret);return 0;}//递归 遍历所有子目录(含子目录的子目录...) 统计其中普通文件的个数void getFileNum(const char *path, int *res){// 打开目录DIR *dir = opendir(path);if (dir == NULL){perror("dir is null");return;}struct dirent *ptr;//循环读取子目录while ((ptr = readdir(dir)) != NULL) //只要没读到末尾(不为NULL) 或出错 就一直循环下去 每调用一次DIR内部指针就自动指向drip中的下个实体{/* code *///获取名称 不能将./ ../算入子目录中char *dname = ptr->d_name;if (strcmp(dname, ".") == 0 || strcmp(dname, "..") == 0) //过滤. .. strcmp==0说明两个字符串相同{continue;}//判断是否是普通文件res++ 还是目录(递归调用)if (ptr->d_type == DT_DIR) //是目录 注意dname仅仅是目录的名字而非完整的路径{//opendir的path要求是完整路径所以递归调用时传的path也要是完整路径char newpath[256];sprintf(newpath, "%s/%s", path, dname); //拼接得到dname对应文件的完整路径 要s/sgetFileNum(newpath, res); //递归调用 遍历子目录内的文件}else if (ptr->d_type == DT_REG) //是普通文件{(*res)++; //要括号*优先级不高}}closedir(dir);return;}
五、文件描述符重定向
/*#include <unistd.h>int dup(int oldfd);系统调用 传入被拷贝的描述符 用未被使用的数值最小的文件描述符作为新的描述符注意拷贝的是文件描述符指向的FILE结构体的内容 而非真的拷贝文件!!!! 新的描述符和老的描述符指向的是同一个文件!!!如果拷贝成功返回这个新的的描述符 失败返回-1并设置errno*/#include <unistd.h>#include <stdio.h>#include <fcntl.h>#include <sys/types.h>#include <sys/stat.h>#include <string.h>int main(){int fd = open("a.txt", O_RDWR | O_CREAT, 0664); //创造新的文件a.txt 因为fd 1,2为流的fd已占用 这个新的fd因该是3int newfd = dup(fd);if (newfd == -1){perror("dup fail");return -1;}print("fd:%d newfd:%d \n", fd, newfd); //fd:3 newfd:4//关闭fd 用newfd向写a.txt写内容 成功 证明dup后 newfd和fd指向的是同一个文件close(fd);char *str = "hello";int ret = write(newfd, str, strlen(str));if (ret == -1){perror("write fail");return -1;}close(newfd);return 0;}
/*#include <unistd.h>int dup2(int oldfd, int newfd);//功能和dup类似用于重定向文件描述符oldfd指向a.txt newfd指向b.txt 在调用函数成功后将newfd->b.txt关闭并且再让newfd指向a.txtoldfd必须是一个有效的 真实指向文件的描述符 如果是个无效的文件描述符 并不会做关闭newfd操作 并且返回-1设置errno如果oldfd和newfd值相同 相当于什么都没做 直接返回newfd并且不会做关闭newfd操作正常返回的就是newfd*/#include <unistd.h>#include <stdio.h>#include <fcntl.h>#include <sys/types.h>#include <sys/stat.h>#include <string.h>int main(){int oldfd = open("a.txt", O_RDWR | O_CREAT, 0664); //创造新的文件a.txt 因为fd 1,2为流的fd已占用 这个新的fd因该是3if (oldfd == -1){perror("open fail");return -1;}int newfd = open("b.txt", O_RDWR | O_CREAT, 0664); //创造新的文件a.txt 因为fd 1,2为流的fd已占用 这个新的fd因该是3if (newfd == -1){perror("open fail");return -1;}print("oldfd:%d newfd:%d \n", oldfd, newfd);int retfd = dup2(oldfd, newfd);print("oldfd:%d newfd:%d \n", oldfd, newfd);if (retfd == -1){perror("dup2 fail");return -1;}//oldfd与newfd在dup2后指向的都是a.txt 下面用newfd来验证一下close(oldfd);char *str = "I'm newfd";int ret = write(newfd, str, strlen(str));if (ret == -1){perror("write fail");return -1;}close(newfd);return 0;}
六、fcntl复制文件描述符 设置/获取文件的状态标志
/*fcntl复制文件描述符 设置/获取文件的状态标志#include <unistd.h>#include <fcntl.h>int fcntl(int fd, int cmd, ... arg );//...表示可变参数 可写也可不写fd 被操作的文件描述符cmd 对文件描述符做什么操作对fd文件做cmd操作cmd:F_DUPFD 复制文件描述符F_GETFL 获取指定文件描述符的文件状态flag文件状态flag在学习open的时候有提到flags:对文件的操作权限设置 以及其他的一些设置O_RDONLY,O_WRONLY,O_RDWR 三个宏分别对应只读,只写,读写。这三个设置是互斥的,1次open只能选1个必选项。O_APPEND 不覆盖在尾部继续追加O_CREAT 如果文件不存在则创建新文件等的可选项在有多个选项时通过|按位或连接如O_RDWR | O_CREATflags是int型占4个字节 为32位其中每一位就是一个标志位每一位代表一种情况(1种标记 可读 可写 可读写 创建新文件)F_SETFL 设置指定文件描述符的文件状态flag(O_DSYNC and O_SYNC文件状态无法通过setfl改变)O_RDONLY,O_WRONLY,O_RDWR 三个宏分别对应只读,只写,读写。这三个设置是互斥的,1次open只能选1个必选项,这三个必选项是无法修改的即无法通过F_SETFL设置!!!并且与文件创建有关的状态 O_CREAT O_EXCL O_NOCTTY O_TRUNC都是无法设置的!!!可选项 O_APPEND 不覆盖在尾部继续追加 O_NONBLOCK 非阻塞 等是可设置的这里的阻塞与非阻塞描述的是函数调用的行为,所谓阻塞方式block,就是进程或是线程执行到这些函数时必须等待某个事件的发生,如果事件没有发生,进程或线程就被阻塞,函数不能立即返回。所谓非阻塞方式non-block,就是进程或线程执行此函数时不必非要等待事件的发生,一旦执行肯定返回,以返回值的不同来反映函数的执行情况,如果事件发生则与阻塞方式相同,若事件没有发生则返回一个代码来告知事件未发生,而进程或线程继续执行,所以效率较高复制传入的fd,并用未被使用的数值最小的文件描述符作为新的描述符 新描述符和fd指向的是同一文件如果复制成功 返回值就是新的描述符*/#include <unistd.h>#include <fcntl.h>#include <stdio.h>int main(){//1复制文件描述符//int fd = open("1.txt", O_RDONLY);// int ret = fcntl(fd, F_DUPFD); //返回复制的那个新的描述符 这个新的描述符和fd都是指向1.txt的//2修改和获取文件状态flagint fd = open("1.txt", O_RDWR);if (fd == -1){perror("open fail");return -1;}//当前是不能写文件的 下面先修改文件状态给flag加入O_APPEND 之后就能在文件尾添加int flag = fcntl(fd, F_GETFL); //先获取当前文件描述符的状态//如果直接给fcntl(fd, F_SETFL,O_APPEND); 那么之前的状态O_RDWR会被直接覆盖 当前线程对此文件不再可读写//flag | O_APPEND 在原基础上追加O_APPENDint ret = fcntl(fd, F_SETFL, flag | O_APPEND);//F_SETFL 失败返回-1 成功返回0close(fd);return 0;}
