Linux:使用vim编辑文件为什么会影响目录的mtime

一个有趣的现象

最近在调试一个问题时,发现了一个有趣的现象:touch一个存在的文件,文件的mtime发生了更新,文件所在目录的mtime不会更新;而使用vim编辑这个文件后再保存,文件和文件所在目录的mtime都会被更新。为什么会这样呢?本文将解释一下这种现象的成因。

背景知识

使用ls -al命令查看文件或目录时,在倒数第二列我们可以看到一个时间,它是文件最近的修改时间modify time(mtime)。而使用stat查看文件信息时,除了可以看到有mtime,还有access time(atime)、change time(ctime)和birth time(btime/crtime):

$ ls -al
total 8
drwxr-xr-x 2 imred imred 4096 Jul 17 22:19 .
drwxr-xr-x 3 imred imred 4096 Jul 17 22:19 ..
-rw-r--r-- 1 imred imred    0 Jul 17 22:19 abc.txt

$ stat abc.txt
  File: abc.txt
  Size: 0               Blocks: 0          IO Block: 4096   regular empty file
Device: 820h/2080d      Inode: 5682        Links: 1
Access: (0644/-rw-r--r--)  Uid: ( 1000/   imred)   Gid: ( 1000/   imred)
Access: 2024-07-17 22:19:20.927884988 +0800
Modify: 2024-07-17 22:19:20.927884988 +0800
Change: 2024-07-17 22:19:20.927884988 +0800
 Birth: 2024-07-17 22:19:20.927884988 +0800

使用man 7 inode可以得到关于这些时间的详细定义:

Last access timestamp (atime)
    stat.st_atime; statx.stx_atime

    This is the file's last access timestamp. It is changed by file accesses, for example, by execve(2), mknod(2), pipe(2), utime(2), and read(2) (of more than zero bytes). Other interfaces, such as mmap(2), may or may not update the atime timestamp

    Some filesystem types allow mounting in such a way that file and/or directory accesses do not cause an update of the atime timestamp. (See noatime, nodiratime, and relatime in mount(8), and related information in mount(2).) In addition, the atime timestamp is not updated if a file is opened with the O_NOATIME flag; see open(2).

File creation (birth) timestamp (btime)
    (not returned in the stat structure); statx.stx_btime

    The file's creation timestamp. This is set on file creation and not changed subsequently.

    The btime timestamp was not historically present on UNIX systems and is not currently supported by most Linux filesystems.

Last modification timestamp (mtime)
    stat.st_mtime; statx.stx_mtime

    This is the file's last modification timestamp. It is changed by file modifications, for example, by mknod(2), truncate(2), utime(2), and write(2) (of more than zero bytes). Moreover, the mtime timestamp of a directory is changed by the creation or deletion of files in that directory. The mtime timestamp is not changed for changes in owner, group, hard link count, or mode.

Last status change timestamp (ctime)
    stat.st_ctime; statx.stx_ctime

    This is the file's last status change timestamp. It is changed by writing or by setting inode information (i.e., owner, group, link count, mode, etc.).

我们在意的是mtime,对于文件来说,它在文件内容被修改时会得到更新,而目录的mtime更新则发生在当目录中的文件创建或删除时。当文件的权限信息等元信息被修改时,mtime并不会更新,而是会更新ctime

成因探讨

test目录下有一个名为abc.txt的文件,我们先看一下其信息:

$ stat . | grep -E "Modify|Birth|Inode"
Device: 820h/2080d      Inode: 4700        Links: 2
Modify: 2024-07-17 22:58:14.387883267 +0800
 Birth: 2024-07-17 22:19:06.287885009 +0800

$ stat abc.txt | grep -E "Modify|Birth|Inode"
Device: 820h/2080d      Inode: 5682        Links: 1
Modify: 2024-07-17 22:19:20.927884988 +0800
 Birth: 2024-07-17 22:19:20.927884988 +0800

testmtime22:58:14abc.txtmtime22:19:20

使用touch更新abc.txtmtime后,再次查看:

$ touch abc.txt

$ stat . | grep -E "Modify|Birth|Inode"
Device: 820h/2080d      Inode: 4700        Links: 2
Modify: 2024-07-17 22:58:14.387883267 +0800
 Birth: 2024-07-17 22:19:06.287885009 +0800

$ stat abc.txt | grep -E "Modify|Birth|Inode"
Device: 820h/2080d      Inode: 5682        Links: 1
Modify: 2024-07-17 23:06:50.407883335 +0800
 Birth: 2024-07-17 22:19:20.927884988 +0800

testmtime仍然为22:58:14abc.txtmtime则被更新为23:06:50

使用vim编辑abc.txt后再次查看:

$ vim abc.txt

$ stat . | grep -E "Modify|Birth|Inode"
Device: 820h/2080d      Inode: 4700        Links: 2
Modify: 2024-07-17 23:09:48.437882556 +0800
 Birth: 2024-07-17 22:19:06.287885009 +0800

$ stat abc.txt | grep -E "Modify|Birth|Inode"
Device: 820h/2080d      Inode: 5732        Links: 1
Modify: 2024-07-17 23:09:48.417882556 +0800
 Birth: 2024-07-17 23:09:48.417882556 +0800

testabc.txtmtime都被更新为23:09:48

为什么使用touch和使用vim会有这种差异呢?难道是因为touch并没有真的改变文件内容导致的?那我们使用echo命令来编辑abc.txt试试:

$ echo xxx > abc.txt

$ stat . | grep -E "Modify|Birth|Inode"
Device: 820h/2080d      Inode: 4700        Links: 2
Modify: 2024-07-17 23:09:48.437882556 +0800
 Birth: 2024-07-17 22:19:06.287885009 +0800

$ stat abc.txt | grep -E "Modify|Birth|Inode"
Device: 820h/2080d      Inode: 5732        Links: 1
Modify: 2024-07-17 23:19:02.617882317 +0800
 Birth: 2024-07-17 23:09:48.417882556 +0800

touch一样,只有abc.txtmtime更新了,所以文件内容是否发生了实质性修改这个因素并不会影响结果,那vim编辑文件时和echo编辑文件时到底有什么差异?

如果你观察够仔细,会发现我在过滤stat输出时,把Inodebtime也打印出来了。对比一下前后几次stat命令的输出,我们会看到,在使用vim编辑abc.txt后,其Inode5682变成了5732btime22:19:20变成了23:09:48。换言之,使用vim编辑后的文件已经不是原来那个文件了。那么期间在test目录下必定创建了新的abc.txt,因此test目录的mtime更新也就是理所应当的了。

那么使用vim编辑一个文件时,在文件系统中究竟发生了什么呢?我们可以使用iwatch命令来研究这个问题。使用iwatch监听test目录,同时使用vim修改abc.txt,查看iwatch的输出:

$ iwatch . -e all_events
[17/Jul/2024 23:31:58] IN_CREATE ./.abc.txt.swp
[17/Jul/2024 23:31:58] IN_CREATE ./.abc.txt.swx
[17/Jul/2024 23:31:58] IN_CLOSE_WRITE ./.abc.txt.swx
[17/Jul/2024 23:31:58] IN_DELETE ./.abc.txt.swx
[17/Jul/2024 23:31:58] * ./.abc.txt.swx is deleted
[17/Jul/2024 23:31:58] IN_CLOSE_WRITE ./.abc.txt.swp
[17/Jul/2024 23:31:58] * ./.abc.txt.swp is closed
[17/Jul/2024 23:31:58] IN_DELETE ./.abc.txt.swp
[17/Jul/2024 23:31:58] * ./.abc.txt.swp is deleted
[17/Jul/2024 23:31:58] IN_CREATE ./.abc.txt.swp
[17/Jul/2024 23:32:02] IN_CREATE ./4913
[17/Jul/2024 23:32:02] IN_CLOSE_WRITE ./4913
[17/Jul/2024 23:32:02] IN_DELETE ./4913
[17/Jul/2024 23:32:02] * ./4913 is deleted
[17/Jul/2024 23:32:02] IN_MOVED_FROM ./abc.txt
[17/Jul/2024 23:32:02] IN_MOVED_TO ./abc.txt~
[17/Jul/2024 23:32:02] * ./abc.txt is moved to ./abc.txt~
[17/Jul/2024 23:32:02] IN_CREATE ./abc.txt
[17/Jul/2024 23:32:02] IN_CLOSE_WRITE ./abc.txt
[17/Jul/2024 23:32:02] * ./abc.txt is closed
[17/Jul/2024 23:32:02] IN_DELETE ./abc.txt~
[17/Jul/2024 23:32:02] * ./abc.txt~ is deleted
[17/Jul/2024 23:32:02] IN_CLOSE_WRITE ./.abc.txt.swp
[17/Jul/2024 23:32:02] IN_DELETE ./.abc.txt.swp
[17/Jul/2024 23:32:02] * ./.abc.txt.swp is deleted

你会发现,vim编辑文件时绝非打开、读取、修改、写入这样的流程,而是会创建多个临时文件。原始的abc.txt文件会被重命名为abc.txt~,然后创建新的abc.txt,写入修改后的内容。如果整个过程中没有发生错误,所有的临时文件最终都会删除,只剩下一个abc.txt。因为涉及到在目录中创建和删除文件的操作,mtime被更新也就理所当然了,这也就解释了文章开头描述的有趣现象。

补充信息

  1. .abc.txt.swp有什么作用?

主要有两个作用,一个是用于存储那些尚未写入磁盘的变更,防止vim崩溃后丢失数据;另一个是为了避免多个vim实例编辑同一个文件,当你使用多个vim实例编辑同一个文件时,你会得到类似这样的警告:

E325: ATTENTION
Found a swap file by the name ".abc.txt.swp"
...

参考链接:https://vi.stackexchange.com/questions/177/what-is-the-purpose-of-swap-files

  1. abc.txt~有什么作用?

这个文件是用来备份原始文件数据的,如果vim直接编辑原始文件,编辑过程中又发生了错误,没有这个备份文件的话,原始文件数据可能就丢失了。

参考链接:https://stackoverflow.com/questions/607435/why-does-vim-save-files-with-a-extension

相关推荐

  1. Linux使用vim编辑文件为什么影响目录mtime

    2024-07-19 08:26:04       18 阅读
  2. Linux使用vim文本编辑器

    2024-07-19 08:26:04       28 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-19 08:26:04       67 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-19 08:26:04       71 阅读
  3. 在Django里面运行非项目文件

    2024-07-19 08:26:04       58 阅读
  4. Python语言-面向对象

    2024-07-19 08:26:04       69 阅读

热门阅读

  1. 数据结构讲解

    2024-07-19 08:26:04       20 阅读
  2. C++:类的定义和实例化

    2024-07-19 08:26:04       21 阅读
  3. NumPy库学习之logspace函数

    2024-07-19 08:26:04       21 阅读
  4. springMVC前后端请求参数绑定和传递

    2024-07-19 08:26:04       17 阅读
  5. C++中的socket编程常用接口

    2024-07-19 08:26:04       19 阅读
  6. Redis实现打卡功能

    2024-07-19 08:26:04       20 阅读
  7. 探索.NET内存之海:垃圾回收的艺术与实践

    2024-07-19 08:26:04       22 阅读
  8. 【.NET全栈】ASP.NET开发Web应用——Web部件技术

    2024-07-19 08:26:04       18 阅读
  9. 基于Gunicorn、Flask和Docker的高并发部署

    2024-07-19 08:26:04       20 阅读
  10. ArcGIS Pro SDK (九)几何 5 多边形

    2024-07-19 08:26:04       21 阅读
  11. SpringBoot集成EasyExcel实现模板写入多个sheet导出

    2024-07-19 08:26:04       21 阅读