众所周知,Linux是一个基于文件的操作系统,因此作为文件本身也就有很多属性,如果想要查看某一个文件的属性有两种方式:命令
和函数
。
虽然有两种方式但是它们对应的名字是相同的,叫做stat
。另外使用file
命令也可以查看文件的一些属性信息。
1. file 命令
该命令用来识别文件类型,也可用来辨别一些文件的编码格式。它是通过查看文件的
头部信息
来获取文件类型,而不是像Windows通过扩展名来确定文件类型的。
命令语法如下:
# 参数在命令中的位置没有限制 $ file 文件名 [参数]
file 命令的参数是可选项, 可以不加, 常用的参数如下表:
参数 | 功能 |
---|---|
-b | 只显示文件类型和文件编码, 不显示文件名 |
-i | 显示文件的 MIME 类型 |
-F | 设置输出字符串的分隔符 |
-L | 查看软连接文件自身文件属性 |
1.1 查看文件类型和编码格式
使用不带任何选项的 file 命令,即可查看指定文件的类型和文件编码信息。
# 空文件 $ file 11.txt 11.txt: empty # 源文件, 编码格式为: ASCII $ file b.cpp b.cpp: C source, ASCII text # 源文件, 编码格式为: UTF-8 robin@OS:~$ file test.cpp test.cpp: C source, UTF-8 Unicode (with BOM) text, with CRLF line terminators # 可执行程序, Linux中的可执行程序为 ELF 格式 robin@OS:~$ file a.out a.out: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/l, for GNU/Linux 2.6.32, BuildID[sha1]=5317ae9fba592bf583c4f680d8cc48a8b58c96a5, not stripped
1.2 只显示文件格式以及编码
使用-b
参数,可以使 file 命令的输出不出现文件名,只显示文件格式以及编码。
# 空文件 $ file 11.txt -b empty # 源文件, 编码格式为: ASCII $ file b.cpp -b C source, ASCII text # 源文件, 编码格式为: UTF-8 robin@OS:~$ file test.cpp -b C source, UTF-8 Unicode (with BOM) text, with CRLF line terminators # 可执行程序, Linux中的可执行程序为 ELF 格式 robin@OS:~$ file a.out -b ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/l, for GNU/Linux 2.6.32, BuildID[sha1]=5317ae9fba592bf583c4f680d8cc48a8b58c96a5, not stripped
1.3 显示文件的 MIME 类型
给file命令添加-i
参数,可以输出文件对应的 MIME 类型的字符串。
MIME(Multipurpose Internet Mail Extensions)多用途互联网邮件扩展类型
。 是设定某种扩展名的文件用一种应用程序来打开的方式类型,当该扩展名文件被访问的时候,浏览器会自动使用指定应用程序来打开
。 多用于指定一些客户端自定义的文件名,以及一些媒体文件打开方式。
# charset 为该文件的字符编码 # 源文件, MIME类型: text/x-c, 字符编码: utf-8 $ file occi.cpp -i occi.cpp: text/x-c; charset=utf-8 # 压缩文件, MIME类型: application/gzip, 字符编码: binary $ file fcgi.tar.gz -i fcgi.tar.gz: application/gzip; charset=binary # 文本文件, MIME类型: text/plain, 字符编码: utf-8 $ file english.txt -i english.txt: text/plain; charset=utf-8 # html文件, MIME类型: text/html, 字符编码: us-ascii $ file demo.html -i demo.html: text/html; charset=us-ascii
1.4 设置输出分隔符
在 file 命令中,文件名和后边的属性信息默认使用冒号(:)分隔,我们可以通过 -F
参数修改分隔符
分隔符可以是单字符也可以是一个字符串,如果分隔符是字符串需要将这个参数值写到引号中(单/双引号都可以)。
# 默认格式输出 $ file english.txt english.txt: UTF-8 Unicode text, with very long lines, with CRLF line terminators # 修改分隔符为字符串 “==>" $ file english.txt -F "==>" english.txt==> UTF-8 Unicode text, with very long lines, with CRLF line terminators $ file english.txt -F '==>' english.txt==> UTF-8 Unicode text, with very long lines, with CRLF line terminators # 修改分隔符为单字符 '=' $ file english.txt -F = english.txt= UTF-8 Unicode text, with very long lines, with CRLF line terminators
1.5 查看软连接文件
软连接文件是一个特殊格式的文件, 查看这种格式的文件可以得到两种结果: 第一种是软连接文件本身的属性信息,另一种是链接文件指向的那个文件的属性信息。 直接通过
file
查看文件属性得到的是链接文件指向的文件的信息 如果添加参数-L
得到的链接文件自身的属性信息。
# 使用 ls 查看链接文件属性信息 $ ll link.lnk lrwxrwxrwx 1 root root 24 Jan 25 17:27 link.lnk -> /root/luffy/onepiece.txt # 使用file直接查看链接文件信息: 得到的是链接文件指向的那个文件的名字 $ file link.lnk link.lnk: symbolic link to `/root/luffy/onepiece.txt' # 使用 file 查看链接文件自身属性信息, 添加参数 -L $ file link.lnk -L link.lnk: UTF-8 Unicode text
2. stat 命令
stat命令显示文件或目录的详细属性信息包括文件系统状态,比ls命令输出的信息更详细。
# 语法格式 # 参数在命令中的位置没有限制 $ stat [参数] 文件或者目录名
关于这个命令的可选参数如下表:
参数 | 功能 |
---|---|
-f | 不显示文件本身的信息,显示文件所在文件系统的信息 |
-L | 查看软链接文件关联的文件的属性信息 |
-c | 查看文件某个单个的属性信息 |
-t | 简洁模式,只显示摘要信息,不显示属性描述 |
2.1 显示所有属性
$ stat english.txt File: 'english.txt' Size: 129567 Blocks: 256 IO Block: 4096 regular file Device: 801h/2049d Inode: 526273 Links: 1 Access: (0644/-rw-r--r--) Uid: ( 1001/ robin) Gid: ( 1001/ robin) Access: 2021-01-31 00:00:36.791490304 +0800 Modify: 2021-01-31 00:00:36.791490304 +0800 Change: 2021-01-31 00:00:36.791490304 +0800 Birth: -
在输出的信息中我们可以看到很多属性,
-
File
: 文件名 -
Size
: 文件大小, 单位是字节 -
Blocks
: 文件使用的数据块总数 -
IO Block
:IO块大小 -
regular file
:文件的实际类型,文件类型不同,该关键字也会变化 -
Device
:设备编号 -
Inode
:Inode号,操作系统用inode编号来识别不同的文件,找到文件数据所在的block,读出数据。 -
Links
:硬链接计数 -
Access
:文件所有者+所属组用户+其他人对文件的访问权限 -
Uid
: 文件所有者名字和所有者ID -
Gid
:文件所有数组名字已经组ID -
Access Time
:表示文件的访问时间。当文件内容被访问时,这个时间被更新 -
Modify Time
:表示文件内容的修改时间,当文件的数据内容被修改时,这个时间被更新 -
Change Time
:表示文件的状态时间,当文件的状态被修改时,这个时间被更新,例如:文件的硬链接链接数,大小,权限,Blocks数等。 -
Birth
: 文件生成的日期
2.2 只显示系统信息
给 stat 命令添加 -f
参数将只显示文件在文件系统中的相关属性信息, 文件自身属性不显示
$ stat luffy/ -f File: "luffy/" ID: 47d795d8889d00d3 Namelen: 255 Type: ext2/ext3 Block size: 4096 Fundamental block size: 4096 Blocks: Total: 10288179 Free: 8991208 Available: 8546752 Inodes: Total: 2621440 Free: 2515927
2.3 软连接文件
使用 stat 查看软链接类型的文件, 默认显示的是这个软链接文件的属性信息, 添加参数 -L
就可以查看这个软连接文件关联的文件的属性信息了。
# 查看软件文件属性 -> 使用 ls -l ls -l link.lnk lrwxrwxrwx 1 root root 24 Jan 25 17:27 link.lnk -> /root/luffy/onepiece.txt # 使用 stat 查看软连接文件属性信息 $ stat link.lnk File: ‘link.lnk’ -> ‘/root/luffy/onepiece.txt’ Size: 24 Blocks: 0 IO Block: 4096 symbolic link Device: fd01h/64769d Inode: 393832 Links: 1 Access: (0777/lrwxrwxrwx) Uid: ( 0/ root) Gid: ( 0/ root) Access: 2021-01-30 23:46:29.922760178 +0800 Modify: 2021-01-25 17:27:12.057386837 +0800 Change: 2021-01-25 17:27:12.057386837 +0800 Birth: - # 使用 stat 查看软连接文件关联的文件的属性信息 $ stat link.lnk -L File: ‘link.lnk’ Size: 3700 Blocks: 8 IO Block: 4096 regular file Device: fd01h/64769d Inode: 660353 Links: 2 Access: (0444/-r--r--r--) Uid: ( 0/ root) Gid: ( 0/ root) Access: 2021-01-30 23:46:53.696723182 +0800 Modify: 2021-01-25 17:54:47.000000000 +0800 Change: 2021-01-26 11:57:00.587658977 +0800 Birth: -
2.4 简洁输出
使用 stat 进行简洁信息输出的可读性不是太好, 所有的属性描述都别忽略了, 如果只想得到属性值, 可以给该命令添加-t
参数:
$ stat luffy/ -t luffy/ 4096 8 41fd 1001 1001 801 662325 8 0 0 1611659086 1580893020 1580893020 0 4096
2.5 单个属性输出
如果每次只想通过 stat 命令得到某一个文件属性, 可以给名添加 -c
参数。
不同的文件属性分别对应一些定义好的特殊符号,想得到哪个属性值,将其指定到参数 -c
后边即可。
属性对应的字符如下表:
格式化字符 | 功能 |
---|---|
%a | 文件的八进制访问权限(#和0是输出标准) |
%A | 人类可读形式的文件访问权限(rwx) |
%b | 已分配的块数量 |
%B | 报告的每个块的大小(以字节为单位) |
%C | SELinux 安全上下文字符串 |
%d | 设备编号 (十进制) |
%D | 设备编号 (十六进制) |
%F | 文件类型 |
%g | 文件所属组组ID |
%G | 文件所属组名字 |
%h | 用连接计数 |
%i | inode编号 |
%m | 挂载点 |
%n | 文件名 |
%N | 用引号括起来的文件名,并且会显示软连接文件引用的文件路径 |
%o | 最佳I/O传输大小提示 |
%s | 文件总大小, 单位为字节 |
%t | 十六进制的主要设备类型,用于字符/块设备特殊文件 |
%T | 十六进制的次要设备类型,用于字符/块设备特殊文件 |
%u | 文件所有者ID |
%U | 文件所有者名字 |
%w | 文件生成的日期 ,人类可识别的时间字符串 – 获取不到信息不显示 |
%W | 文件生成的日期 ,自纪元以来的秒数 (参考 %X )– 获取不到信息不显示 |
%x | 最后访问文件的时间, 人类可识别的时间字符串 |
%X | 最后访问文件的时间, 自纪元以来的秒数(从1970.1.1开始到最后一次文件访问的总秒数) |
%y | 最后修改文件内容的时间, 人类可识别的时间字符串 |
%Y | 最后修改文件内容的时间, 自纪元以来的秒数(参考 %X ) |
%z | 最后修改文件状态的时间, 人类可识别的时间字符串 |
%Z | 最后修改文件状态的时间, 自纪元以来的秒数(参考 %X ) |
仔细阅读上表可以知道:文件的每一个属性都有一个或者多个与之对应的格式化字符,这样就可以精确定位所需要的属性信息了,举几个例子作为参考:
$ stat occi.cpp -c %a 644 $ stat occi.cpp -c %A -rw-r--r-- # 使用 ls -l 验证权限 $ ll occi.cpp -rw-r--r-- 1 robin robin 1406 Jan 31 00:00 occi.cpp # 0664 $ stat link.lnk -c %N 'link.lnk' -> '/home/robin/english.txt' $ stat link.lnk -c %y 2021-01-31 10:48:52.317846411 +0800
3. stat/lstat 函数
stat
/lstat
函数的功能和 stat 命令的功能是一样的, 只不过是应用场景不同。
这两个函数的区别在于处理软链接文件的方式上
-
lstat()
: 得到的是软连接文件本身的属性信息 -
stat()
: 得到的是软链接文件关联的文件的属性信息
//函数原型 #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> int stat(const char *pathname, struct stat *buf); int lstat(const char *pathname, struct stat *buf);
- 参数:
- pathname: 文件名, 要获取这个文件的属性信息
- buf: 传出参数, 文件的信息被写入到了这块内存中
- 返回值: 函数调用成功返回 0,调用失败返回 -1
这个函数的第二个参数是一个结构体类型, 这个结构体相对复杂, 通过这个结构体可以存储得到的文件的所有属性信息, 结构体原型如下:
struct stat { dev_t st_dev; // 文件的设备编号 ino_t st_ino; // inode节点 mode_t st_mode; // 文件的类型和存取的权限, 16位整形数 -> 常用 nlink_t st_nlink; // 连到该文件的硬连接数目,刚建立的文件值为1 uid_t st_uid; // 用户ID gid_t st_gid; // 组ID dev_t st_rdev; // (设备类型)若此文件为设备文件,则为其设备编号 off_t st_size; // 文件字节数(文件大小) --> 常用 blksize_t st_blksize; // 块大小(文件系统的I/O 缓冲区大小) blkcnt_t st_blocks; // block的块数 time_t st_atime; // 最后一次访问时间 time_t st_mtime; // 最后一次修改时间(文件内容) time_t st_ctime; // 最后一次改变时间(指属性) };
3.1 获取文件大小
下面调用 stat()
函数, 以代码的方式演示一下如何得到某个文件的大小:
#include <sys/stat.h> int main() { // 1. 定义结构体, 存储文件信息 struct stat myst; // 2. 获取文件属性 english.txt int ret = stat("./english.txt", &myst); if(ret == -1) { perror("stat"); return -1; } printf("文件大小: %d\n", (int)myst.st_size); return 0; }
3.2 获取文件类型
文件的类型信息存储在 struct stat
结构体的st_mode
成员中, 它是一个 mode_t
类型, 本质上是一个16位的整数。
Linux API中为我们提供了相关的宏函数,通过对应的宏函数可以直接判断出文件是不是某种类型,这些信息都可以通过 man 文档(man 2 stat
)查询到。
相关的宏函数原型如下:
// 类型是存储在结构体的这个成员中: mode_t st_mode; // 这些宏函数中的 m 对应的就是结构体成员 st_mode // 宏函数返回值: 是对应的类型返回-> 1, 不是对应类型返回0 S_ISREG(m) is it a regular file? - 普通文件 S_ISDIR(m) directory? - 目录 S_ISCHR(m) character device? - 字符设备 S_ISBLK(m) block device? - 块设备 S_ISFIFO(m) FIFO (named pipe)? - 管道 S_ISLNK(m) symbolic link? (Not in POSIX.1-1996.) - 软连接 S_ISSOCK(m) socket? (Not in POSIX.1-1996.) - 本地套接字文件
在程序中通过宏函数判断文件类型, 实例代码如下:
int main() { // 1. 定义结构体, 存储文件信息 struct stat myst; // 2. 获取文件属性 english.txt int ret = stat("./hello", &myst); if(ret == -1) { perror("stat"); return -1; } // 判断文件类型 if(S_ISREG(myst.st_mode)) { printf("这个文件是一个普通文件...\n"); } if(S_ISDIR(myst.st_mode)) { printf("这个文件是一个目录...\n"); } if(S_ISLNK(myst.st_mode)) { printf("这个文件是一个软连接文件...\n"); } return 0; }
3.2 获取文件权限
用户对文件的操作权限也存储在 struct stat
结构体的st_mode
成员中, 在这个16位的整数中不同用户的权限存储位置如下图
如果想知道有没有相关权限可以通过按位与(&)操作将这个标志位值取出判断即可。
Linux 中为我们提供了用于不同用户不同权限判定使用的宏,具体信息如下:
关于变量 st_mode: - st_mode -- 16位整数 ○ 0-2 bit -- 其他人权限 - S_IROTH 00004 读权限 100 - S_IWOTH 00002 写权限 010 - S_IXOTH 00001 执行权限 001 - S_IRWXO 00007 掩码, 过滤 st_mode中除其他人权限以外的信息 ○ 3-5 bit -- 所属组权限 - S_IRGRP 00040 读权限 - S_IWGRP 00020 写权限 - S_IXGRP 00010 执行权限 - S_IRWXG 00070 掩码, 过滤 st_mode中除所属组权限以外的信息 ○ 6-8 bit -- 文件所有者权限 - S_IRUSR 00400 读权限 - S_IWUSR 00200 写权限 - S_IXUSR 00100 执行权限 - S_IRWXU 00700 掩码, 过滤 st_mode中除文件所有者权限以外的信息 ○ 12-15 bit -- 文件类型 - S_IFSOCK 0140000 套接字 - S_IFLNK 0120000 符号链接(软链接) - S_IFREG 0100000 普通文件 - S_IFBLK 0060000 块设备 - S_IFDIR 0040000 目录 - S_IFCHR 0020000 字符设备 - S_IFIFO 0010000 管道 - S_IFMT 0170000 掩码,过滤 st_mode中除文件类型以外的信息 ############### 按位与操作举例 ############### 1111 1111 1111 1011 # st_mode 0000 0000 0000 0100 # S_IROTH & ---------------------------------------- 0000 0000 0000 0000 # 没有任何权限
通过仔细阅读上边提供的宏信息, 我们可以知道处理使用它们得到用户对文件的操作权限, 还可以用于判断文件的类型(判断文件类型的第二种方式),具体操作方式可以参考如下代码:
#include <sys/stat.h> int main() { // 1. 定义结构体, 存储文件信息 struct stat myst; // 2. 获取文件属性 english.txt int ret = stat("./hello", &myst); if(ret == -1) { perror("stat"); return -1; } printf("文件大小: %d\n", (int)myst.st_size); // 判断文件类型 if(S_ISREG(myst.st_mode)) { printf("这个文件是一个普通文件...\n"); } if(S_ISDIR(myst.st_mode)) { printf("这个文件是一个目录...\n"); } if(S_ISLNK(myst.st_mode)) { printf("这个文件是一个软连接文件...\n"); } // 文件所有者对文件的操作权限 printf("文件所有者对文件的操作权限: "); if(myst.st_mode & S_IRUSR) { printf("r"); } if(myst.st_mode & S_IWUSR) { printf("w"); } if(myst.st_mode & S_IXUSR) { printf("x"); } printf("\n"); return 0; }