文件操作

一、Linux哲学核心思想

“一切皆文件” 是 Linux 哲学的核心思想之一,深刻影响着 Linux 系统的设计、架构与使用方式。这一思想将系统中的各种资源,包括硬件设备、软件组件、进程间通信机制等,都抽象成文件的形式进行管理和操作。其优势和体现如下:

统一的操作接口:在 Linux 系统里,无论是普通文件、目录,还是硬件设备,如硬盘(/dev/sda)、串口(/dev/ttyS0 ),亦或是进程通信的管道(pipe)、套接字(socket)等,都能通过文件系统的接口来访问。这使得用户和应用程序可以使用统一的系统调用,如 open ()、read ()、write ()、close () 等函数来操作各类资源,极大地简化了编程和系统管理的复杂度。以读取硬盘数据和读取普通文件数据为例,应用程序无需为不同的设备或资源使用不同的接口,降低了开发和维护成本。

灵活的权限管理:借助文件系统的权限机制,Linux 可以方便地对各种资源进行访问控制。每个文件都有所有者、所属组以及相应的读(r)、写(w)、执行(x)权限设置。这种权限管理方式不仅适用于普通文件,对设备文件同样有效。比如,通过设置设备文件的权限,可以限制普通用户对某些硬件设备的访问,只有特定用户或用户组才能操作,从而增强了系统的安全性和稳定性。

方便的设备管理:在 Linux 的设备管理中,所有硬件设备都被映射为文件系统中的设备文件。这意味着,用户和应用程序可以像操作普通文件一样操作设备。例如,向打印机设备文件(如 /dev/lp0 )写入数据,就可以实现打印功能;对块设备文件(如 /dev/sda )进行读写操作,就能管理存储设备上的数据。这种将设备抽象为文件的方式,使得设备的管理和使用更加直观、便捷,也方便了系统对设备的统一管理和调度。

简化进程间通信:进程间通信(IPC)是 Linux 系统中进程交互的重要机制。在 “一切皆文件” 的思想下,管道(pipe)和套接字(socket)等 IPC 机制也被视为文件。进程可以通过读写管道文件或套接字文件来实现数据的传输和交互。例如,一个进程可以向管道文件写入数据,另一个进程则从该管道文件读取数据,从而实现进程间的通信。这种方式利用了文件操作的特性,简化了进程间通信的实现过程,提高了系统的整体性能和可扩展性。

二、文件的类型示例

ls -l 输出信息的首字符,代表文件的类型

1.普通文件:在 Ubuntu 系统里,许多配置文件、日志文件等都属于普通文件。比如/etc/hosts文件,它用于配置主机名和 IP 地址的映射关系。执行ls -l /etc/hosts,可能得到类似如下结果:

-rw-r--r-- 1 root root 203 Mar 15  2023 /etc/hosts

开头的-表示这是一个普通文件。文件所有者root有读写权限(rw-),所属组用户和其他用户只有读权限(r--)。文件大小为 203 字节,最后修改时间是 2023 年 3 月 15 日

  1. 目录文件:/etc目录是系统配置文件存放的重要目录。运行ls -ld /etc,目录的标识如下:
drwxr-xr-x  133 root root  4096 Mar 15  2023 /etc

最前面的d表明/etc是一个目录文件。文件所有者root对其有读写执行权限(rwx),所属组用户和其他用户有读和执行权限(r - x)。目录大小显示为 4096 字节(这是目录本身的元数据大小,不包含目录内文件的大小),最后修改时间是 2023 年 3 月 15 日

  1. 字符设备文件:/dev/tty是一个常用的字符设备文件,代表当前的终端设备。执行ls -l /dev/tty,示例输出为:
crw-rw-rw- 1 root tty 5, 0 Mar 15  2023 /dev/tty

开头的c表示它是字符设备文件。所有者root,所属组tty,主设备号为 5,次设备号为 0 。这两个设备号用于系统识别和驱动对应的设备 。

  1. 块设备文件:/dev/sda通常表示系统中的第一块 SATA 或 SCSI 硬盘设备。使用ls -l /dev/sda,可能得到:
brw-rw---- 1 root disk 8, 0 Mar 15  2023 /dev/sda

开头的b代表这是块设备文件。所有者是root,所属组是disk,主设备号 8 和次设备号 0 用于确定设备的驱动和具体实例 。

  1. 符号链接文件:在 Ubuntu 中,/etc/alternatives目录下有很多符号链接文件,用于管理系统中可替代的软件版本或配置。例如查看/etc/alternatives/awk
lrwxrwxrwx 1 root root 22 Mar 15  2023 /etc/alternatives/awk -> /usr/bin/gawk

开头的l表示这是符号链接文件。它指向/usr/bin/gawk,文件大小为 22 字节(是链接本身的大小,不是目标文件的大小),权限表示所有用户都有读、写和跟随链接的权限 。

  1. 管道文件:在进程间通信时会用到管道文件。虽然系统中不会预先存在很多有名管道文件,但可以通过命令创建来查看。例如,使用mkfifo my_pipe创建一个名为my_pipe的管道文件,然后ls -l my_pipe会显示:
prw-rw-r-- 1 ubuntu ubuntu 0 Mar 20 06:34 my_pipe

开头的p表示这是一个管道文件。它主要用于进程间的数据传递,文件大小显示为 0 字节 。

  1. 套接字文件:在/var/run目录下,存放着许多与系统服务相关的套接字文件。例如查看/var/run/dbus/system_bus_socket
srw-rw-rw- 1 root root 0 Mar 14 09:14 /var/run/dbus/system_bus_socket

开头的s表明这是一个套接字文件。所有者是root,所属组也是root ,用于进程间的通信,特别是在系统服务之间传递消息。

💡

注意: 以下内容在后续章节中详细讲解,暂时先了解一下 Shell脚本中,使用if判断文件类型的方法:

if [ -e /etc/hosts ] #任意文件是否存在
if [ -f /etc/hosts ] #普通文件是否存在
if [ -d /etc ]  #目录文件是否存在
if [ -L /etc/alternatives/awk ] #软连接文件是否存在
if [ -S /var/run/dbus/system_bus_socket ]   #套接字文件是否存在
if [ -c /dev/tty ]  #字符文件是否存在
if [ -p my_pipe ]  #管道文件是否存在
if [ -b /dev/sda ]  #快设备文件是否存在

三、文件权限

(一)文件权限的重要性

在 Linux 系统中,文件权限至关重要。它就像是文件的 “门禁系统”,决定了谁能访问文件,以及以何种方式访问,比如读取文件内容、修改文件,或者执行文件(如果文件是可执行程序)。合理设置文件权限,既能保证用户数据的安全,防止他人随意篡改或查看,又能确保系统的正常运行,避免因权限不当导致的程序错误或安全漏洞。

(二)文件权限的表示方法

符号表示法

在 Linux 中,文件权限用符号表示为r(读)、w(写)、x(执行)。对于文件所有者、所属组以及其他用户,分别有对应的读、写、执行权限设置。

例如,rw-r--r--表示文件所有者有读和写的权限,所属组用户只有读权限,其他用户也只有读权限。如果某个权限不存在,就用-表示 。

数字表示法

数字表示法是用数字来代表不同的权限组合。r对应数字 4,w对应 2,x对应 1,没有该权限则为 0。将所有者、所属组、其他用户的权限数字相加,就能得到一个三位数的权限表示。

比如rwxr-xr-x,所有者权限为4+2+1 = 7,所属组权限为4+0+1 = 5,其他用户权限为4+0+1 = 5,所以用数字表示就是755

(三)常用文件权限操作命令

1.chmod命令用于改变文件或目录的权限。

符号模式修改:

例如,要给文件test.txt的所有者添加执行权限,可以使用chmod u+x test.txt 。这里u代表所有者(user)+x表示添加执行权限。如果要给所属组和其他用户添加读权限,可以用chmod g+r,o+r test.txtg代表所属组(group)o代表其他用户(others)

数字模式修改:

使用数字模式更为简洁。比如要将文件test.txt的权限设置为rwxr - xr - x(即 755),可以执行chmod 755 test.txt 。这种方式在批量设置权限时非常方便。

2.stat命令可以显示文件的详细状态信息,其中包括文件权限。执行stat 文件名,会得到类似下面的输出:

  File: ‘test.txt’
  Size: 1024       	Blocks: 8          IO Block: 4096   regular file
Device: 801h/2049d	Inode: 123456      Links: 1
Access: (0644/-rw-r--r--)  Uid: ( 1000/  user)   Gid: ( 1000/  user)
Access: 2023-01-01 10:00:00.000000000 +0000
Modify: 2023-01-01 10:01:00.000000000 +0000
Change: 2023-01-01 10:01:00.000000000 +0000
 Birth: -

Access一行中,0644是数字表示的权限,-rw-r--r--是符号表示的权限,同时还能看到文件的所有者和所属组等信息 。

3.lsattr命令用于查看文件的特殊权限属性,这些属性可以对文件的操作进行更细致的限制。 例如,有些文件设置了a属性(append only),表示只能追加内容,不能删除或覆盖原有内容。执行lsattr 文件名,输出可能如下:

----a--------e-- test.txt

这里的a就是特殊权限属性之一,e也是一种特殊属性,代表扩展属性(extended attributes)

4.chattr命令用于设置文件的特殊权限属性。比如要给文件test.txt添加只能追加内容的属性,可以执行chattr +a test.txt 。如果要取消这个属性,就用chattr -a test.txt 。特殊权限属性对于保护系统关键文件或有特殊需求的文件非常有用。

(四)文件权限操作示例

1.设置普通文件权限

假设创建了一个新文件new_file.txt,默认权限可能是-rw-rw-r--。如果希望只有文件所有者有读写执行权限,所属组和其他用户只有读权限,可以这样操作:

chmod 744 new_file.txt

使用ls -l查看,会看到权限变为-rwxr--r--

2.查看和修改特殊权限属性

查看文件test.txt的特殊权限属性:

lsattr test.txt

给test.txt添加不可删除和重命名的属性(i属性):

chattr +i test.txt

此时,即使是文件所有者,也无法删除或重命名该文件,尝试删除时会提示权限不足。若要恢复正常操作,取消该属性即可: bash

chattr -i test.txt

四、文件的唯一id 在 Linux 系统中,文件的id 通常指的是 inode(索引节点)。inode 是一个数据结构,它存储了文件的元信息,这些信息与文件内容本身分开存储,用于标识和管理文件。

inode 的作用:inode 包含了文件的许多关键属性,如文件的类型(是普通文件、目录、设备文件等)、文件的权限、文件的大小、文件的创建时间、修改时间、访问时间,以及指向文件数据块的指针等。系统通过 inode 来定位和访问文件的数据,而不是直接通过文件名。文件名只是指向 inode 的一个链接,这使得在同一文件系统中,多个文件名可以指向同一个 inode,即实现 “硬链接”。例如,当用户访问一个文件时,系统首先根据文件名找到对应的 inode,然后从 inode 中获取文件数据的存储位置等信息,进而读取或修改文件内容 。

inode 号码:每个文件都有一个唯一的 inode 号码,在文件系统中用于区分不同的文件。通过ls -i命令可以查看文件的 inode 号码。例如,在某个目录下执行ls -i,输出结果如下:

123456  file1.txt
789012  file2.txt

这里的123456789012就是file1.txtfile2.txt对应的 inode 号码。即使文件名不同,只要 inode 号码相同,就表示它们指向同一个文件 。

inode 与文件系统的关系:inode 在文件系统的管理中起着核心作用。文件系统在创建时,会预先分配一定数量的 inode,其数量在文件系统创建后一般固定不变。当创建一个新文件时,文件系统会分配一个空闲的 inode 给该文件,并为文件数据分配相应的数据块,同时在 inode 中记录这些数据块的位置。在删除文件时,对应的 inode 会被标记为空闲,以便重新使用 。

inode 的局限性:虽然 inode 对文件管理至关重要,但它也有一些局限性。由于 inode 数量在文件系统创建时就固定了,如果系统中创建了大量小文件,可能会导致 inode 耗尽,尽管磁盘空间还有剩余,但却无法再创建新文件。此时,需要清理一些不必要的文件或重新规划文件系统来解决问题 。

五、软链接和硬链接

在 Linux 系统中,链接是一种特殊的文件,它指向其他文件或目录。链接主要分为硬链接和软链接(符号链接),它们在功能和特性上有明显的区别,并且系统对链接数存在一定的限制。

硬链接

硬链接实际上是为文件创建了一个别名,它和原文件共享相同的 inode 号码 。这意味着它们指向的是同一个文件数据块,对其中任何一个文件的修改,都会直接反映在另一个文件上 。硬链接的特点如下:

共享 inode:硬链接和原文件具有相同的 inode,在文件系统中,inode 是文件的唯一标识,包含了文件的元数据(如文件的权限、大小、创建时间等)和数据块的指针。因此,硬链接和原文件在这些方面完全相同,改变其中一个文件的内容,另一个文件也会相应改变 。

不能跨越文件系统:硬链接只能在同一文件系统内创建。这是因为不同文件系统的 inode 编号是独立的,无法在不同文件系统之间创建指向同一 inode 的硬链接 。

增加链接数:每创建一个硬链接,文件的链接数就会增加 1。可以使用ls -l命令查看文件的链接数,链接数显示在文件权限信息的第二列。例如,一个文件初始链接数为 1,创建一个硬链接后,链接数变为 2

ubuntu@hk1-133-20-101:~$ ls /etc/alternatives/awk -l
lrwxrwxrwx 1 root root 13 Feb 24 18:44 /etc/alternatives/awk -> /usr/bin/gawk

删除原文件不影响硬链接:当删除原文件时,只要还有硬链接存在,文件的数据就不会被删除 。只有当所有指向该文件的硬链接都被删除后,文件的数据才会真正被删除 。

软链接(符号链接)

软链接也叫符号链接,它是一个独立的文件,有自己的 inode 和数据块。软链接的数据块中存储的是指向目标文件的路径信息。软链接的特点如下:

有独立的 inode:与硬链接不同,软链接有自己独立的 inode,它和目标文件的 inode 不同 。

可以跨越文件系统:软链接可以指向不同文件系统中的文件,甚至可以指向不存在的文件(这种情况下软链接会处于 “broken” 状态)。

不增加目标文件链接数:创建软链接不会增加目标文件的链接数,软链接只是一个指向目标文件的路径指针 。

依赖目标文件:如果目标文件被删除,软链接就会变成无效链接,访问软链接会提示文件不存在 。

链接数限制

在 Linux 系统中,对文件的链接数存在一定限制:

系统层面的限制:每个文件系统在创建时,会分配一定数量的 inode,这就限制了文件的最大链接数 。因为每个链接都需要占用一个 inode,当系统中的文件和链接数量接近 inode 总数时,可能无法再创建新的链接 。不同的文件系统,inode 数量的默认值不同,例如,ext4 文件系统在创建时,可以根据磁盘大小和其他参数来确定 inode 的数量 。

文件自身的限制:单个文件的链接数也有上限。这个上限通常是一个较大的数值(如在大多数系统中,单个文件的链接数上限为 32767 ),在一般情况下,很难达到这个上限。但在某些特殊场景下,比如大量使用硬链接的情况下,可能会遇到链接数达到上限的问题 。

示例

创建硬链接:使用ln命令创建硬链接。

touch file1.txt
ln file1.txt hard_link.txt
ls -li file1.txt hard_link.txt
291699 -rw-rw-r-- 2 ubuntu ubuntu 0 Mar 20 07:25 file1.txt
291699 -rw-rw-r-- 2 ubuntu ubuntu 0 Mar 20 07:25 hard_link.txt

创建软链接:使用ln -s命令创建软链接。

ln -s file1.txt soft_link.txt
ls -li file1.txt soft_link.txt
291699 -rw-rw-r-- 2 ubuntu ubuntu 0 Mar 20 07:25 file1.txt
291700 lrwxrwxrwx 1 ubuntu ubuntu 9 Mar 20 07:28 soft_link.txt -> file1.txt