Bash:使用>运算符写入并追加到文件

在Bash脚本中,如果我echo "abc" > file.txtecho "abc" >> file.tx我保证,当脚本的下一行执行时, "abc"存在于file.txt

澄清:我想到的是,在我echo "some text" > file.txt之后,是否可以确定,然后继续执行脚本中的下一行,并调用其他一些过程,也可以从我的脚本读取file.txt ,将该过程从文件中读取"some text" ? 另外,一旦我写入文件,并终止脚本,脚本终止执行刷新?

直接的答案是肯定的 。 如果你的输出重定向启动的过程已经完成(为此,shell只是等待(2)系统调用),完整的过程映像已经从内核内部表中删除,所有的文件描述符都关闭了(所以它不能做任何更多的系统调用)。

一般来说,你不能保证一个进程正在用户空间中进行缓冲。 内核保证,一旦执行了write(2)系统调用,所有系统进程看到的文件内容就包含了这些数据。 为了达到这个目的,内核只是在内核内存中阻塞inode映像,以允许进程在系统调用时写入内核分配的缓冲区,而不会受到可能同时发生的其他内核活动的干扰。 当读取进程(2)写入(2)时,这授予对文件内容的独占访问。 并且inode被整个系统调用阻塞(使调用成为原子)。 之后,所有的文件内容都会更新到其他进程。

当进程正在写入时,inode锁定是保证写入系统调用完全在其他事情发生之前完成的,如:

  • 其他一些进程正在尝试写入相同的文件。
  • 一些其他的进程取消了打开的文件的链接(所有进程访问这个文件之前,取消链接将继续访问它,即使不存在于文件系统)即使其他进程创建一个同名的文件,所有的写去旧的,打开的文件。
  • 其他一些进程试图读取(它将读取所有写入的数据或根本不读取,但不会进行部分读取)
  • 其他一些进程更改文件的权限(写入文件的权限仅在打开(2)时被检查)。
  • 其他一些进程会截断该文件。 这将被及时序列化,使写入发生在系统调用之前或之后(但不在中间)。
  • 其他一些进程尝试卸载此文件所属的文件系统。

所有这些可能性(以及更多)都必须在内核代码中被考虑,以保证数据是一致的。

由于正在执行的进程已经完成,答案是写入的所有数据对其他进程都是可见的(它已经被刷新了,所以不能在关闭(2)之后和写入(2)之后都不会新建)没有数据被刷新到磁盘。


另一方面,您使用的示例并不完全适用于这里的解释: echo通常是一个内部shell命令,由shell在其用户空间中执行。 因此,这可能使事情发生不同,因为使系统调用的进程是shell,并且在命令之前没有退出(2) 。 但即使在这种情况下,shell可以产生一个新的进程来执行这个重定向的命令(而不是调用exec(2)来执行一个不同的命令),并在执行完成后退出(2) ,所有的文件描述符都会也关闭。 这也使得这里的例子也适用。

在这种情况下,shell不会为命令产生一个新的进程(至少在bash(1)中也有这种情况),这种行为就好像新进程已经产生一样,所以shell有(2)文件描述符的可靠性。

一旦包含重定向到file.txt的命令终止,写入到stdout的任何内容都会出现在file.txt ,并且会被后续的读取file.txt进程看到。

但是有一些警告:

  1. 另一个同时执行的进程完全有可能删除或覆盖文件。

  2. Unix / Linux不保证写入文件的数据将在文件关闭时立即提交到永久存储器。 [注1]因此,如果存储文件的计算机崩溃并在写入和读取之间重新启动,读取任务可能看不到崩溃前写入的数据。

  3. 如果写入任务异常终止,则可能不会将其输出缓冲区刷新为stdout。 所以如果任务崩溃,可能没有任何东西会被写入到文件中。

总之,最好是说在bash命令过程中写入文件的数据(无论是直接命名还是通过重定向)只要第一个任务正常终止,其他运行进程就不会被后续命令看到正在操作同一个文件,并且主机在执行后续命令之前不会崩溃。


笔记

  1. 通常情况下,文件没有立即提交到永久存储并不重要,因为操作系统必须像在文件中存在挂起的修改一样进行操作。 此外,在正常关机期间,操作系统将提交所有未决的文件系统修改。 但是,如果主机崩溃,或在重新启动时出现一些不可恢复的文件系统错误,则可能会丢失某些文件系统修改。

    以上所有内容适用于存储文件的机器,这些文件可能不同于在通过网络文件系统访问文件的情况下写入和读取文件的机器。