《Linux编程》学习笔记 ·004【文件I/O操作】
注:前言、目录见 https://blog.csdn.net/qq_44220418/article/details/108428971
文章目录
- 一、文件系统简介
- 1、索引节点
- 2、文件系统组成
- 3、文件
- (1).类型
- (2).文件权限
- [1].文件权限
- [2].表示方法
- (3).访问文件
- 二、文件基本I/O操作
- 1、文件描述符
- 2、常用系统调用
- (1).`open`
- (2).`close`
- (3).`write`
- (4).`read`
- (5).`lseek`
- (6).使用示例
- (7).探索与练习
- 三、目录操作
- 1、目录文件
- 2、常用系统调用
- (1).`opendir`
- (2).`readdir`
- (3).`closedir`
- (4).`rewinddir`
- (5).`seekdir`
- (6).`telldir`
- (7).`mkdir`
- (8).`rmdir`
- (9).`getcwd`
- (10).`chdir`
- (11).`rename`
- (12).使用示例
- 四、文件/目录的属性
- 1、常用函数
- (1).`stat`
- (2).`lstat`
- (3).`fstat`
- 2、常用宏
- (1).文件类型宏
- (2).文件权限宏
- 3、使用示例
一、文件系统简介
1、索引节点
Linux系统采用按名存取的方式访问文件
除了文件名以外,文件的主要属性信息都存放在inode节点中
目录文件中保存着文件名和索引节点的对应关系【可用命令ls -i
查看文件/目录的inode号】
Linux系统中文件包括两部分
- 索引节点inode
\qquad记录文件属性信息(除了文件名)【可用命令ls -l
查看】
\;
\qquad文件属性包括{文件类型访问权限文件主人组长度访问日期……\begin{cases} 文件类型 \\ 访问权限 \\ 文件主人 \\ 组 \\ 长度 \\ 访问日期 \\ …… \end{cases}⎩⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎨⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎧文件类型访问权限文件主人组长度访问日期……
\;
\qquad索引节点的结构如下图所示:
\qquad\qquad - 数据块
\qquad存放文件具体内容
注:硬链接文件的inode与源文件的inode相同
更多关于inode节点、软链接与硬链接的区别相关的知识可以参考博文 Linux文件索引节点inode
2、文件系统组成
一个简单的Unix文件系统组成
\qquad
\qquad引导块:用于引导该分区内操作系统的引导程序
\qquad超级块:UFS的重要参数,如文件系统的{块索引节点总数空闲块空闲索引节点数\begin{cases} 块 \\ 索引节点总数 \\ 空闲块 \\ 空闲索引节点数 \\ \end{cases}⎩⎪⎪⎪⎨⎪⎪⎪⎧块索引节点总数空闲块空闲索引节点数
\qquad索引节点表(i节点):文件系统中索引节点的集合
\qquad数据块:存储文件数据
Linux文件系统——Ext2
\qquad
\qquad第一个是引导块,其他空间分成各个块组
\qquad每个块组中{超级块块组描述符,数据块位图,索引节点位图,索引节点,数据块\begin{cases} 超级块 \\ 块组描述符,数据块位图,索引节点位图,索引节点,数据块 \end{cases}{超级块块组描述符,数据块位图,索引节点位图,索引节点,数据块
3、文件
(1).类型
文件类型{普通文件(rugular file)目录文件(directory)字符设备文件(character device)块设备文件(block device)FIFO文件(fifo)符号链接文件(symbolic link)socket套接字文件(socket)\begin{cases} 普通文件 & (\text{rugular file})\\ 目录文件 & (\text{directory})\\ 字符设备文件 & (\text{character device})\\ 块设备文件 & (\text{block device})\\ \text{FIFO}文件 & (\text{fifo})\\ 符号链接文件 & (\text{symbolic link})\\ \text{socket}套接字文件 & (\text{socket})\\ \end{cases}⎩⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎨⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎧普通文件目录文件字符设备文件块设备文件FIFO文件符号链接文件socket套接字文件(rugular file)(directory)(character device)(block device)(fifo)(symbolic link)(socket)
(2).文件权限
[1].文件权限
Linux中的用户分为三类{user/owner文件拥有者group用户组other其他用户\begin{cases} \text{user/owner} & 文件拥有者 \\ \text{group} & 用户组 \\ \text{other} & 其他用户 \\ \end{cases}⎩⎪⎨⎪⎧user/ownergroupother文件拥有者用户组其他用户
Linux文件对三类用户都有三种访问权限{r可读w可写x可执行\begin{cases} \text{r} & 可读 \\ \text{w} & 可写 \\ \text{x} & 可执行 \\ \end{cases}⎩⎪⎨⎪⎧rwx可读可写可执行
另外,Linux文件还有三种特殊的权限{setuidsetgidsticky\begin{cases} \text{setuid} \\ \text{setgid} \\ \text{sticky} \\ \end{cases}⎩⎪⎨⎪⎧setuidsetgidsticky
这里就不对这三种特殊权限做具体介绍了,想知道更详细的可以去看博文 Linux文件特殊权限——SetUID、SetGID、Sticky BIT
Linux文件的索引节点里面有一个st_mode
字段,记录文件类型及权限
该字段共 161616 位{高4位文件类型低12位文件权限\begin{cases} 高4位 & 文件类型 \\ 低12位 & 文件权限 \\ \end{cases}{高4位低12位文件类型文件权限
12位文件权限{特殊权限{setuidsetgidsticky文件拥有者{r可读权限w可写权限x可执行权限用户组{r可读权限w可写权限x可执行权限其他用户{r可读权限w可写权限x可执行权限\begin{cases} 特殊权限 & \begin{cases} \text{setuid} \\ \text{setgid} \\ \text{sticky} \\ \end{cases} \\ \\ 文件拥有者 & \begin{cases} \text{r} & 可读权限 \\ \text{w} & 可写权限 \\ \text{x} & 可执行权限 \\ \end{cases} \\ \\ 用户组 & \begin{cases} \text{r} & 可读权限 \\ \text{w} & 可写权限 \\ \text{x} & 可执行权限 \\ \end{cases} \\ \\ 其他用户 & \begin{cases} \text{r} & 可读权限 \\ \text{w} & 可写权限 \\ \text{x} & 可执行权限 \\ \end{cases} \\ \end{cases}⎩⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎨⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎧特殊权限文件拥有者用户组其他用户⎩⎪⎨⎪⎧setuidsetgidsticky⎩⎪⎨⎪⎧rwx可读权限可写权限可执行权限⎩⎪⎨⎪⎧rwx可读权限可写权限可执行权限⎩⎪⎨⎪⎧rwx可读权限可写权限可执行权限
如下图所示:
[2].表示方法
文件权限的表示方法
- 字母
字母 权限 r
可读 w
可写 x
可执行 -
无 如:
rwxr-xr-x
就表示了{拥有者对文件可读、可写、可执行用户组对文件可读、可执行其他用户对文件可读、可执行\begin{cases} 拥有者对文件可读、可写、可执行 \\ 用户组对文件可读、可执行 \\ 其他用户对文件可读、可执行 \\ \end{cases}⎩⎪⎨⎪⎧拥有者对文件可读、可写、可执行用户组对文件可读、可执行其他用户对文件可读、可执行 - 数字(9位二进制 / 3位八进制)
和上面的字母类似,对应位的二进制{1有权限0无权限\begin{cases} 1 & 有权限 \\ 0 & 无权限 \\ \end{cases}{10有权限无权限
如:111 101 101
就相当于rwxr-xr-x
,755
就相当于rwxr-xr-x
- 宏(S_IPwww模式)
S_I
固定,P
可为R
或W
或X
,www
可为USR
或GRP
或OTH
,多种权限可用或运算连接
如:S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH
就相当于rwxr-xr-x
注:编程中作为参数时,只能使用数字/宏
(3).访问文件
每打开一次文件
- 系统增加一个
file
表项 - 进程增加一个
fd
表项(以文件描述符为下标),通过指针指向file
表项 - 用户通过文件描述符和系统调用访问该文件
二、文件基本I/O操作
1、文件描述符
用户空间中任何打开的文件都被分配一个唯一的非负整数,标识该打开文件,即文件描述符
进程默认打开的三个文件描述符
- 标准输入
0
- 标准输出
1
- 标准错误输出
2
会选用文件描述符中未使用的最小值
2、常用系统调用
常用的系统调用如下
系统调用 | 说明 |
---|---|
open
|
打开/新建文件 |
close
|
关闭文件 |
creat
|
新建文件 |
read
|
读文件 |
write
|
写文件 |
lseek
|
定位文件偏移量 |
(1).open
定义在<fcntl.h>
中
函数原型如下:
int open( const char *path, int flags);
int open( const char *path, int flags, mode_t perms);
- 功能
-
打开一个已经存在的文件时,参数
perms
可以省略 -
创建并打开一个不存在的文件时,参数
perms
必填 - 参数
-
参数 说明 path
文件路径 flags
文件打开方式(可用或运算 |
连接多种打开方式)
关于flags
可选的宏参数可参考博客 https://blog.csdn.net/ArchyLi/article/details/78937937perms
文件权限(4位八进制,仅创建时需要) - 返回值
- 成功:返回最小可用的文件描述符(当前文件所用的文件描述符)
-
失败:返回
-1
(置errno
)
(2).close
定义在<unistd.h>
中
函数原型如下:
int close(int fd );
- 功能
- 关闭文件,释放文件描述符,使之可再利用
- 参数
-
参数 说明 fd
文件描述符 - 返回值
-
成功:返回
0
-
失败:返回
-1
(置errno
)
注:当一个进程终止时,内核会自动检查并回收该进程所有的文件描述符
(3).write
定义在<unistd.h>
中
函数原型如下:
ssize_t write(int fd, const void *buf, size_t nbytes);
- 功能
-
将
buf
所指缓冲区中的nbytes
个字节写入文件描述符fd
所指示的已打开文件中 - 注意点
- 写操作从当前文件偏移量处开始写数据,写完后,文件偏移量也将后移成功写入的字节数(因此不移动读写指针读不到刚刚写入的内容)
-
若调用
open
打开文件时,读写指针初始指向文件开头
\qquad若flags
包含O_APPEND
,则每次写入前,读写指针都将移动到文件末尾 -
由于物理介质空间不足等原因将会使得
write
的返回值小于计划要写入文件的字节个数 - 参数
-
参数 说明 fd
文件描述符 buf
要写入的数据 nbytes
要写入数据的字节数 - 返回值
- 成功:返回已写入的字节数
-
失败:返回
-1
(置errno
)
(4).read
定义在<unistd.h>
中
函数原型如下:
ssize_t read(int fd, void *buf, size_t nbytes);
- 功能
-
从一文件描述符
fd
对应的已打开文件中,读取nbytes
字节的数据放入buf
中 - 注意点
- 读操作从当前文件偏移量处开始读数据,读完后,当前文件偏移也将改变成功读出的字节个数
-
若调用
open
打开文件时,读写指针初始指向文件开头 -
有时读到的数据可能比希望读的字节少,但系统不会置
errno
,因为这不是错误(如文件已经到结尾),需要用户自己去推测问题所在 - 参数
-
参数 说明 fd
文件描述符 buf
保存读取数据的缓冲区 nbytes
要读取数据的字节数 - 返回值
- 成功:返回已读取的字节数
-
失败:返回
-1
(置errno
)
(5).lseek
定义在<unistd.h>
中
函数原型如下:
off_t lseek(int fd, off_t pos, int whence);
- 文件偏移量
- 文件偏移量是一个非负整数,它用于标识下一次读或写文件的位置
-
除了
O_APPEND
模式打开文件外,读/写操作都从当前文件偏移量处开始 -
每
open
一次文件,就能得到一个新的打开文件表项,因此每次open
都可以得到一个独立的文件偏移量 - 功能
- 移动文件读写指针(文件偏移量)
- 参数
-
参数 说明 fd
文件描述符 pos
相对于 whence
的位置的偏移量whence
SEEK_SET
:代表文件开始,即设文件偏移量为pos
SEEK_CUR
:代表当前位置,即设文件偏移量为当前值+pos
SEEK_END
:代表文件结尾,即设文件偏移量为文件长度+pos
- 返回值
- 成功:返回当前文件偏移量
-
失败:返回
-1
(置errno
)
(6).使用示例
示例1:创建文件权限为rw-r--r--
的文件test.txt
(初始时不存在),在其末尾写入内容
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>int main()
{// 创建并打开文件test.txtint fd = open("test.txt", O_RDWR | O_APPEND | O_CREAT, 0644);if (fd == -1){perror("open");exit(1);}// 写入文件char str1[] = "123456789012345678901234567890\n";if (write(fd, str1, strlen(str1)) != -1){printf("写入成功\n");}// 关闭文件close(fd);return 0;
}
程序输出的内容如下
写入成功
生成的test.txt
文件内容如下
123456789012345678901234567890
示例2:从刚刚 示例1 生成的test.txt
中读取至多 999999 字节的数据
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>int main()
{// 打开文件int fd = open("test.txt", O_RDWR);if (fd == -1){perror("open");exit(1);}// 读取文件char buf[100];read(fd, buf, sizeof(buf) - 1);printf("读取到的字符串是\n%s\n", buf);// 关闭文件close(fd);return 0;
}
示例3:创建文件权限为rw-r--r--
的文件test3.txt
(初始时不存在),利用lseek
函数定位,写入、读取文件内容
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>int main()
{// 创建int fd3 = open("test3.txt", O_RDWR | O_CREAT, 0644);char str2[] = "newnewnewnewnewnewnewnew\n";char str3[] = "test\n";char buf[100];// 向文件末尾写入数据lseek(fd3, 0, SEEK_END);write(fd3, str2, strlen(str2));// 从文件开头读取数据lseek(fd3, 0, SEEK_SET);read(fd3, buf, sizeof(buf) - 1);printf("读取到的字符串3是\n%s\n", buf);// 向文件开头写入数据lseek(fd3, 0, SEEK_SET);write(fd3, str3, strlen(str3));// 从文件开头读取数据lseek(fd3, 0, SEEK_SET);read(fd3, buf, sizeof(buf) - 1);printf("读取到的字符串4是\n%s\n", buf);close(fd3);return 0;
}
程序输出的内容如下
读取到的字符串3是
newnewnewnewnewnewnewnew读取到的字符串4是
test
wnewnewnewnewnewnew
生成的test3.txt
文件内容如下
test
wnewnewnewnewnewnew
显然这个最终结果是将test3.txt
之前的前5个字符"newne"
修改成了"test\n"
(7).探索与练习
探索1:对同一文件的不同fd
的操作是否会造成相互影响?
已存在test2.txt
,内容如下:
1234321222222
编写了如下C源程序对其进行探究:
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>int main()
{char str1[] = "123456789012345678901234567890\n";char buf[100];int fd1 = open("test2.txt", O_RDWR | O_APPEND);int fd2 = open("test2.txt", O_RDONLY);write(fd1, str1, strlen(str1));write(fd1, str1, strlen(str1));write(fd1, str1, strlen(str1));read(fd2, buf, sizeof(buf) - 1);printf("读取到的字符串2是\n%s\n", buf);close(fd1);close(fd2);return 0;
}
程序输出的内容如下
读取到的字符串2是
1234321222222123456789012345678901234567890
123456789012345678901234567890
1234567890123456789012
修改后的test2.txt
文件内容如下
1234321222222123456789012345678901234567890
123456789012345678901234567890
123456789012345678901234567890
显然,fd1
和fd2
都是已打开文件test2.txt
的文件描述符
fd1
的写操作改变了fd1
的文件偏移量,而没有改变fd2
的文件偏移量(从读操作正常可见)
这也就验证了 每次open
都可以得到一个独立的文件偏移量
练习1:将键盘输入的一批字符串(以quit
结束)写入到文件test.txt
中,然后再读出并显示到屏幕上
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>int main()
{char str[999];char buf[100];// 打开文件int fd = open("test.txt", O_CREAT | O_RDWR, 0644);// 循环读入并写入scanf("%s", &str);while (strcmp(str, "quit")){// 写入write(fd, str, strlen(str));scanf("%s", &str);}// 将文件偏移量定位到文件开头lseek(fd, 0, SEEK_SET);// 循环读取文件内容printf("读取到的写入内容如下:\n");while (read(fd, buf, sizeof(buf) - 1)){printf("%s", buf);}printf("\n");// 关闭文件close(fd);return 0;
}
程序输入的内容如下
1234
abc
quit
程序输出的内容如下
读取到的写入内容如下:
1234abc
创建的test.txt
文件内容如下
1234abc
三、目录操作
1、目录文件
Linux系统中,目录也是以文件的方式存储,在目录文件中存储了该目录下的文件的信息,包括{文件名文件inode编号文件类型⋯\begin{cases} 文件名 \\ 文件inode编号 \\ 文件类型 \\ \cdots \\ \end{cases}⎩⎪⎪⎪⎨⎪⎪⎪⎧文件名文件inode编号文件类型⋯,姑且把这些信息称为一个文件的索引信息
如果想要找到某个文件,首先需要找到这个文件所在目录的目录文件
2、常用系统调用
常用的系统调用如下
系统调用 | 说明 |
---|---|
opendir
|
打开目录 |
readdir
|
读取目录 |
telldir
|
返回当前打开的目录的下次读取位置 |
seekdir
|
设置目录流的读取位置为指定位置 |
rewinddir
|
重置目录流的读取位置为起始位置 |
closedir
|
关闭目录 |
(1).opendir
定义在<dirent.h>
中
函数原型如下:
DIR * opendir(const char *dir_path);
- 功能
-
打开一个目录,返回一个
DIR
指针,从而创建一个到目录的连接 - 参数
-
参数 说明 dir_path
目录路径 - 返回值
- 成功:返回指向参数目录的指针
-
失败:返回
NULL
(置errno
)
(2).readdir
定义在<dirent.h>
中
函数原型如下:
struct dirent * readdir(DIR *dir);
- 功能
-
从
DIR
指针中读取一个目录项(文件/目录)的信息,并后移指针对应目录的读取位置 - 参数
-
参数 说明 dir
已打开目录对应的 DIR
指针 - 返回值
- 成功:返回相应目录项的结构体指针
-
失败:返回
NULL
(目录项读取完成)
(3).closedir
定义在<dirent.h>
中
函数原型如下:
int closedir(DIR * dir);
- 功能
- 关闭目录
- 参数
-
参数 说明 dir
已打开目录对应的 DIR
指针 - 返回值
-
成功:返回
0
-
失败:返回
-1
(置errno
)
(4).rewinddir
定义在<dirent.h>
中
函数原型如下:
void rewinddir(DIR * dir);
- 功能
- 重置目录指针读取位置到起始位置
- 参数
-
参数 说明 dir
已打开目录对应的 DIR
指针
(5).seekdir
定义在<dirent.h>
中
函数原型如下:
void seekdir(DIR * dir, off_t offset);
- 功能
- 设置目录指针读取位置到指定位置
- 参数
-
参数 说明 dir
已打开目录对应的 DIR
指针
(6).telldir
定义在<dirent.h>
中
函数原型如下:
off_t telldir(DIR * dir);
- 功能
-
返回目录指针下次读取的位置(可以理解为一个从
0
开始的索引号) - 参数
-
参数 说明 dir
已打开目录对应的 DIR
指针 - 返回值
- 成功:返回当前目录指针下次读取的位置
-
失败:返回
-1
(置errno
)
(7).mkdir
定义在<sys/stat.h>
中
函数原型如下:
int mkdir(char * pathname, mode_t mode);
- 功能
- 创建目录,并指定其权限模式
- 参数
-
参数 说明 pathname
创建目录的路径 mode
创建目录的权限模式
Tips:关于mode
的选项,可以参考博客 mkdir()函数、mode_t参数 - 返回值
-
成功:返回
0
-
失败:返回
-1
(置errno
)
(8).rmdir
定义在<unistd.h>
中
函数原型如下:
int rmdir(char * pathname);
- 功能
- 删除目录
- 参数
-
参数 说明 pathname
要删除目录的路径 - 返回值
-
成功:返回
0
-
失败:返回
-1
(置errno
)
(9).getcwd
定义在<unistd.h>
中
函数原型如下:
char* getcwd(char *buf, size_t size);
- 功能
- 获取进程当前所处的工作目录的绝对路径,存入参数字符数组中
- 参数
-
参数 说明 buf
存储路径的字符数组 size
buf
所指向字符数组的字节数 - 返回值
- 成功:返回存储空间的首地址
-
失败:返回
NULL
(置errno
)
(10).chdir
定义在<unistd.h>
中
函数原型如下:
int chdir(const char *pathname);
- 功能
- 切换当前进程所处的工作目录
- 注意点
- 对其他进程、调用该进程的进程的当前工作目录均没有影响
- 参数
-
参数 说明 pathname
要切换到的工作目录路径 - 返回值
-
成功:返回
0
-
失败:返回
-1
(置errno
)
(11).rename
定义在<unistd.h>
中
函数原型如下:
int rename(const char *from, const char *to);
- 功能
- 移动并重命名文件
- 注意点
-
rename
并不真正复制文件中的数据,它只是将文件的链接从一个目录移动到另一个目录中,这个过程数据本身并没有移动 - 参数
-
参数 说明 from
源文件路径 to
新文件路径 - 返回值
-
成功:返回
0
-
失败:返回
-1
(置errno
)
(12).使用示例
示例4:尝试使用上述若干系统调用读取、创建、删除、更改、查看目录
#include <stdio.h>
#include <dirent.h>
#include <sys/stat.h>
#include <unistd.h>int main()
{// 打开目录testdirDIR * dir_ptr = opendir("testdir");struct dirent * dir;// 获取下次读取位置if (dir_ptr != NULL)printf("下次读取的位置是:%ld\n\n", telldir(dir_ptr));// 获取目录下所有文件的名称printf("第一次打印:\n");if (dir_ptr != NULL)while ((dir = readdir(dir_ptr)) != NULL)printf("%s\n", dir->d_name);printf("\n");// 重定位,重新读取printf("第二次打印:\n");if (dir_ptr != NULL){rewinddir(dir_ptr);while ((dir = readdir(dir_ptr)) != NULL)printf("%s\n", dir->d_name);}printf("\n");// 重定位,重新读取printf("第三次打印:\n");if (dir_ptr != NULL){seekdir(dir_ptr, 3);while ((dir = readdir(dir_ptr)) != NULL)printf("%s\n", dir->d_name);}printf("\n");// 关闭目录closedir(dir_ptr);// 创建目录int res = mkdir("./testmk", 0777);if (!res)printf("创建目录成功\n\n");// 删除目录res = rmdir("./testmk");if (!res)printf("删除目录成功\n\n");// 获取当前工作目录char str[100];getcwd(str, sizeof(str) - 1);printf("当前工作目录:\n%s\n\n", str);// 改变并重新获取当前工作目录res = chdir("./testdir/");if (!res){getcwd(str, sizeof(str) - 1);printf("改变后的当前工作目录:\n%s\n\n", str);}// 移动并重命名目录/文件// (注:因为已经改变了当前工作目录,所以现在已经跳转到了testdir目录下,再用"./testdir"就不管用了)int res2 = rename("./subdir/", "./newname/");int res3 = rename("./a.txt", "./b.txt");if (!res2 && !res3){printf("重命名成功\n");dir_ptr = opendir("./");printf("重命名后的打印:\n");if (dir_ptr != NULL)while ((dir = readdir(dir_ptr)) != NULL)printf("%s\n", dir->d_name);printf("\n");}return 0;
}
程序输出的内容如下
下次读取的位置是:0第一次打印:
.
..
12.txt
subdir
a.txt第二次打印:
.
..
12.txt
subdir
a.txt第三次打印:
12.txt
subdir
a.txt创建目录成功删除目录成功当前工作目录:
/home/excious/工程/目录操作改变后的当前工作目录:
/home/excious/工程/目录操作/testdir重命名成功
重命名后的打印:
.
..
12.txt
newname
b.txt
四、文件/目录的属性
文件与目录的属性存储与inode节点中,可通过系统调用stat
获取文件或者目录的属性,存入struct stat
结构中
结构体stat
至少包含了以下文件信息:
struct stat
{dev_t st_dev, // 包含该文件的设备ID号ino_t st_ino, // 文件的inode号mode_t st_mode, // 文件类型及权限模式nlink_t st_nlink, // 文件的链接数uid_t st_uid, // 文件所有者的用户IDgid_t st_gid, // 文件的组IDdev_t st_rdev, // 如果文件为字符或块设备时的设备IDoff_t st_size, // 如果文件为普通文件时的文件字节数time_t st_atime, // 最近的访问时间time_t st_mtime, // 最近的数据修改时间time_t st_ctime, // 最近的文件状态改变时间blksize_t st_blksize, // 该对象文件系统相关的最佳I/O块大小blkcnt_t st_blocks // 系统为此文件所分配的数据块数
}
1、常用函数
(1).stat
定义在<sys/stat.h>
中
函数原型如下:
int stat(char *pathname, struct stat *buf);
- 功能
- 获取文件的详细信息
- 注意点
- 路径中的文件若是符号链接文件,则会获取其所链接到的文件的详细信息
- 参数
-
参数 说明 pathname
要切换到的工作目录路径 buf
要存入文件详细信息的结构指针 - 返回值
-
成功:返回
0
-
失败:返回
-1
(置errno
)
(2).lstat
定义在<sys/stat.h>
中
函数原型如下:
int lstat(char *pathname, struct stat *buf);
- 功能
- 获取文件的详细信息
- 注意点
- 路径中的文件若是符号链接文件,则会获取该文件本身的详细信息
- 参数
-
参数 说明 pathname
要切换到的工作目录路径 buf
要存入文件详细信息的结构指针 - 返回值
-
成功:返回
0
-
失败:返回
-1
(置errno
)
(3).fstat
定义在<sys/stat.h>
中
函数原型如下:
int fstat(int fd, struct stat *buf);
- 功能
- 获取已打开文件的详细信息
- 参数
-
参数 说明 fd
已打开文件的文件描述符 buf
要存入文件详细信息的结构指针 - 返回值
-
成功:返回
0
-
失败:返回
-1
(置errno
)
2、常用宏
(1).文件类型宏
定义在<usr/include/bits/stat.h>
、<sys/stat.h>
中
文件信息中的模式st_mode
是一个161616位的二进制数,由高位(第151515位)到低位(第000位)分别是444为文件类型、333位特殊权限、333位用户权限、333位组权限、333位其他用户权限
C语言中已经定义好的文件类型宏如下表所示
最高四位二进制数 | 文件类型常量(八进制) | 文件类型 |
---|---|---|
0100
|
S_IFDIR 0040000
|
目录文件 |
0010
|
S_IFCHR 0020000
|
字符设备文件 |
0110
|
S_IFBLK 0060000
|
块设备文件 |
1000
|
S_IFREG 0100000
|
普通文件 |
1010
|
S_IFLNK 0120000
|
符号链接文件 |
1100
|
S_IFSOCK 0140000
|
Socket文件 |
0001
|
S_IFIFO 0010000
|
命名管道文件 |
可以把st_mode
和相应的mask
进行按位与,与上述相应的宏进行比较,判断文件类型
-
关于掩码
mask
: -
要判断文件类型,因为161616位文件模式中高444位是文件类型,因此应取
mask
为1111000000000000
,即mask = 0170000
,该常量
C语言中也同样预定义好了用于判断文件类型的函数宏如下,直接将st_mode
传入即可判断:
#define __S_ISTYPE(mode, mask) (((mode) & __S_IFMT) == (mask))#define S_ISDIR(mode) __S_ISTYPE((mode), __S_IFDIR)
#define S_ISCHR(mode) __S_ISTYPE((mode), __S_IFCHR)
#define S_ISBLK(mode) __S_ISTYPE((mode), __S_IFBLK)
#define S_ISREG(mode) __S_ISTYPE((mode), __S_IFREG)#ifdef __S_IFIFO
# define S_ISFIFO(mode) __S_ISTYPE((mode), __S_IFIFO)
#endif#ifdef __S_IFLNK
# define S_ISLNK(mode) __S_ISTYPE((mode), __S_IFLNK)
#endif
(2).文件权限宏
定义在<usr/include/bits/stat.h>
、<sys/stat.h>
中
文件信息中的模式st_mode
低999位表示文件权限
C语言中已经定义好的文件权限宏如下
#define S_IRUSR 0000400 // 文件所有者读权限
#define S_IWUSR 0000200 // 文件所有者写权限
#define S_IXUSR 0000100 // 文件所有者执行权限
#define S_IRWXU (__S_IREAD | __S_IWRITE | __S_IEXEC) // 文件所有者读写执行权限#define S_IRGRP (S_IRUSR >> 3) // 组用户读权限
#define S_IWGRP (S_IWUSR >> 3) // 组用户写权限
#define S_IXGRP (S_IXUSR >> 3) // 组用户执行权限
#define S_IRWXG (S_IRWXI >> 3) // 组用户读写执行权限#define S_IROTH (S_IRGRP >> 3) // 其他用户读权限
#define S_IWOTH (S_IWGRP >> 3) // 其他用户写权限
#define S_IXOTH (S_IXGRP >> 3) // 其他用户执行权限
#define S_IRWXO (S_IRWXO >> 3) // 其他用户读写执行权限
3、使用示例
示例5:尝试使用上述若干系统调用获取文件信息、判断文件类型和文件权限
#include <stdio.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>int main(int argc, char *argv[])
{struct stat buf;int res = stat("./test.txt", &buf);if (res != -1){printf("文件的\t【设备ID号】\t\t\t为\t【%ld】\n", buf.st_dev);printf("文件的\t【inode号】\t\t\t为\t【%ld】\n", buf.st_ino);printf("文件的\t【类型及权限模式】\t\t为\t【%d】\n", buf.st_mode);printf("文件的\t【链接数】\t\t\t为\t【%ld】\n", buf.st_nlink);printf("文件的\t【所有者用户ID】\t\t为\t【%d】\n", buf.st_uid);printf("文件的\t【组ID】\t\t\t为\t【%d】\n", buf.st_gid);printf("文件的\t【字节数】\t\t\t为\t【%ld】\n", buf.st_size);printf("文件的\t【最近访问时间】\t\t为\t【%ld】\n", buf.st_atime);printf("文件的\t【最近数据修改时间】\t\t为\t【%ld】\n", buf.st_mtime);printf("文件的\t【最近状态改变时间】\t\t为\t【%ld】\n", buf.st_ctime);printf("文件的\t【系统相关最佳I/O块大小】\t为\t【%ld】\n", buf.st_blksize);printf("文件的\t【被系统分配到的数据块数】\t为\t【%ld】\n", buf.st_blocks);}printf("==============================================================\n");int fd = open("test.txt", O_RDONLY);struct stat buf2;int res2 = fstat(fd, &buf2);if (res2 != -1){printf("文件的\t【设备ID号】\t\t\t为\t【%ld】\n", buf2.st_dev);printf("文件的\t【inode号】\t\t\t为\t【%ld】\n", buf2.st_ino);printf("文件的\t【类型及权限模式】\t\t为\t【%d】\n", buf2.st_mode);printf("文件的\t【链接数】\t\t\t为\t【%ld】\n", buf2.st_nlink);printf("文件的\t【所有者用户ID】\t\t为\t【%d】\n", buf2.st_uid);printf("文件的\t【组ID】\t\t\t为\t【%d】\n", buf2.st_gid);printf("文件的\t【字节数】\t\t\t为\t【%ld】\n", buf2.st_size);printf("文件的\t【最近访问时间】\t\t为\t【%ld】\n", buf2.st_atime);printf("文件的\t【最近数据修改时间】\t\t为\t【%ld】\n", buf2.st_mtime);printf("文件的\t【最近状态改变时间】\t\t为\t【%ld】\n", buf2.st_ctime);printf("文件的\t【系统相关最佳I/O块大小】\t为\t【%ld】\n", buf2.st_blksize);printf("文件的\t【被系统分配到的数据块数】\t为\t【%ld】\n", buf2.st_blocks);}close(fd);printf("==============================================================\n");struct stat buf3;int res3 = stat("./test.txt", &buf3);if (res3 != -1){if ((buf3.st_mode & S_IFMT) == S_IFREG)printf("文件test.txt是一个普通文件\n");if (S_ISREG(buf3.st_mode))printf("文件test.txt是一个普通文件\n");}printf("==============================================================\n");struct stat buf4;int res4 = stat("./test.txt", &buf4);if (res4 != -1){int mode = buf4.st_mode;char str_res[] = "----------";// 判断文件类型if (S_ISDIR(mode)) str_res[0] = 'd';if (S_ISCHR(mode)) str_res[0] = 'c';if (S_ISBLK(mode)) str_res[0] = 'b';// 判断用户权限if (mode & S_IRUSR) str_res[1] = 'r';if (mode & S_IWUSR) str_res[2] = 'w';if (mode & S_IXUSR) str_res[3] = 'x';// 判断组权限if (mode & S_IRGRP) str_res[4] = 'r';if (mode & S_IWGRP) str_res[5] = 'w';if (mode & S_IXGRP) str_res[6] = 'x';// 判断其他用户权限if (mode & S_IROTH) str_res[7] = 'r';if (mode & S_IWOTH) str_res[8] = 'w';if (mode & S_IXOTH) str_res[9] = 'x';// 输出文件类型和权限printf("文件类型和权限为%s\n", str_res);}return 0;
}
程序输出的内容如下
文件的 【设备ID号】 为 【33】
文件的 【inode号】 为 【33875】
文件的 【类型及权限模式】 为 【33188】
文件的 【链接数】 为 【1】
文件的 【所有者用户ID】 为 【1000】
文件的 【组ID】 为 【1000】
文件的 【字节数】 为 【7】
文件的 【最近访问时间】 为 【1618807263】
文件的 【最近数据修改时间】 为 【1618807248】
文件的 【最近状态改变时间】 为 【1618807260】
文件的 【系统相关最佳I/O块大小】 为 【4096】
文件的 【被系统分配到的数据块数】 为 【8】
==============================================================
文件的 【设备ID号】 为 【33】
文件的 【inode号】 为 【33875】
文件的 【类型及权限模式】 为 【33188】
文件的 【链接数】 为 【1】
文件的 【所有者用户ID】 为 【1000】
文件的 【组ID】 为 【1000】
文件的 【字节数】 为 【7】
文件的 【最近访问时间】 为 【1618807263】
文件的 【最近数据修改时间】 为 【1618807248】
文件的 【最近状态改变时间】 为 【1618807260】
文件的 【系统相关最佳I/O块大小】 为 【4096】
文件的 【被系统分配到的数据块数】 为 【8】
==============================================================
文件test.txt是一个普通文件
文件test.txt是一个普通文件
==============================================================
文件类型和权限为-rw-r--r--
《Linux编程》学习笔记 ·004【文件I/O操作】相关推荐
- Linux系统学习笔记:文件描述符标志
文件描述符标志的概念 文件描述符标志(目前就只有一个close-on-exec): 它仅仅是一个标志,当进程fork一个子进程的时候,在子进程中调用了exec函数时就用到了这个标志.意义是执行exec ...
- Linux centOS学习笔记(文件处理命令)
touch: [文件名] 在当前目录创建一个文件(文件名中有空格需要加双引号,不然会创建两个文件) [/路径/文件名] 在指定路径创建一个文件 cat: [/路径/文件名] 查看文件内容(不适合浏览长 ...
- Linux命令学习笔记(一)目录操作
目录操作 在桌面建立一个名为test的文件夹 clear 清除屏幕 pwd 查看当前工作目录 cd /home 进入 '/ home' 目录' cd ~ 打开当前用户目录 cd / 根目录 cd - ...
- shell脚本编程学习笔记(四)shell操作数据库
一.数据库基本操作 1)登录mysql服务器:mysql -u root -p 密码 2)查看数据库:show databases 3)查看表:show tales from db; 4)查看表结构: ...
- Linux基本命令学习笔记--文件查看、下载、查找
1.Linux基本命令学习笔记–(文件查看) cat -整个查看 -n:行号 >(创建.覆盖) >>(追加)a.txt<<EOF:编写内容 内容 EOF (定格写:结束) ...
- Linux与C++11多线程编程(学习笔记)
多线程编程与资源同步 在Windows下,主线程退出后,子线程也会被关闭; 在Linux下,主线程退出后,系统不会关闭子线程,这样就产生了僵尸进程 3.2.1创建线程 Linux 线程的创建 #inc ...
- [Linux网络编程学习笔记]索引
一.Linux基本知识 [学习笔记]Linux平台的文件I/O操作 [学习笔记]Linux平台的文件,目录及操作 [Linux学习笔记]标准输入输出 [Linux学习笔记]进程概念及控制 [Linux ...
- 《Linux Shell编程学习笔记之一》
<Linux Shell编程学习笔记之一> 前言 由于自己一直在Windows上面编程,用linux用的比较少,学习linux还是本科大二学的一点点知识.因此自己就准备花点时间来熟悉下li ...
- Linux Shell编程学习笔记(2)
Linux Shell编程学习笔记(2015-7-12) 分类:linux shell Shell变量 一:关于Shell变量 Shell是一种弱类型的语言,变量存储的一切值都是字符串.Shell ...
- linux系统管理学习笔记之八---linux文件与目录的管理及权限
linux系统管理学习笔记之八---linux文件与目录的管理及权限 2010-01-05 09:00:49 标签:权限 管理 文件目录 linx [推送到技术圈] 版权声明:原创作品,允许转载,转载 ...
最新文章
- 标题和描述需注意什么才更有利于SEO首页优化?
- python多个for的执行顺序-python_装饰器篇(多个装饰器下的执行顺序)
- mysql清理连接数缓存,MySQL连接池、线程缓存、线程池的区别
- Sentinel 规则持久化到 apollo 配置中心
- python rest 框架_python-更新用户REST框架Django
- mysql数据库的注释语句是_coding++ :MySQL 使用 SQL 语句查询数据库所有表注释已经表字段注释...
- winform flash
- metasploit连接postgresql的问题(U盘启动kali linux)
- Cannot use v-for on stateful component root element because it renders multiple elements.
- Linux meset
- 分享两个好玩好看的特效
- pfamscan 的使用_48个在线分析使用工具
- 小程序分享图片给好友,到朋友圈,保存到本地
- android 测光模式,安卓手机里的专业模式究竟该怎么拍?
- 汉堡包菜单_神圣的汉堡包!
- Vue无缝滚动轮播插件vue-seamless-scroll
- 王和勇计算机软件,图像空间中的鉴别型局部线性嵌入方法
- python合并word表格单元格_Python-Excel转word表格并合并流程
- android MediaPlayer 完成播放后无法再次播放
- vue 如何实现多个路由共用同一个页面组件