为什么将文件输出重定向至其自身会产生一个空白文件?
在 Bash 中,为什么
less foo.txt > foo.txt
和
fold foo.txt > foo.txt
生成一个空的foo.txt
? 由于诸如 之类的附加操作less eggs.py >> eggs.py
会生成 中的两个文本副本eggs.py
,因此人们可能认为覆盖操作会生成一个文本副本。
请注意,我并不是说这是一个错误,它更有可能指向 Unix 的某些深层问题。
答案1
当您使用时>
,文件将以截断模式打开,因此在命令尝试读取文件之前其内容会被删除。
当您使用 时>>
,文件将以追加模式打开,因此现有数据将被保留。但是,在这种情况下,使用同一个文件作为输入和输出仍然非常危险。如果文件足够大而无法容纳读取输入缓冲区大小,则其大小可能会无限增长,直到文件系统已满(或达到磁盘配额)。
如果您想要使用不支持就地修改的命令将文件同时用作输入和输出,则可以使用以下几种解决方法:
使用中间文件,完成后覆盖原始文件,并且仅在运行实用程序时没有发生错误时才覆盖原始文件(这是最安全和最常见的方法)。
fold foo.txt > fold.txt.$$ && mv fold.txt.$$ foo.txt
避免使用中间文件,因为如果发生错误或中断,可能会造成部分或全部数据丢失。在此示例中,的内容
foo.txt
作为输入传递给子壳(括号内)前文件被删除。由于子 shell 在读取数据时保持打开状态,因此前一个 inode 仍处于活动状态。内部实用程序(此处fold
)写入的文件虽然具有相同的名称(foo.txt
),但指向不同的 inode,因为旧目录条目已被删除,因此从技术上讲,在此过程中有两个具有相同名称的不同“文件”。当子 shell 结束时,旧 inode 被释放,其数据丢失。请注意确保您有足够的空间同时临时存储旧文件和新文件,否则您将丢失数据。(rm foo.txt; fold > foo.txt) < foo.txt
答案2
在应用程序有机会读取文件之前,shell 会打开文件进行写入。打开文件进行写入会截断它。
答案3
在 bash 中,流重定向运算... > foo.txt
符清空foo.txt
在评估左操作数之前。
可以使用命令替换并打印其结果作为解决方法。此解决方案所需的额外字符比其他答案少:
printf '%s\n' "$(less foo.txt)" > foo.txt
注意:此命令不会保留 中的任何尾随换行符foo.txt
。请参阅下面的评论部分以获取更多信息
在这里,命令替换$(...)
被评估前流重定向操作符>
,从而保存信息。