bash - 为什么在管道中读取和写入同一文件会产生不可靠的结果?

标签 bash sed io-redirection cat

我有一堆包含许多空白行的文件,并且想要删除任何重复的空白行以使读取文件更容易。我编写了以下脚本:

#!/bin/bash
for file in * ; do cat "$file" | sed 's/^ \+//' | cat -s > "$file" ; done

但是,这产生了非常不可靠的结果,大多数文件完全变空,只有少数文件具有预期结果。更重要的是,每次我重试时,有效的文件似乎都会随机变化,因为每次运行都会正确编辑不同的文件。这是怎么回事?

注意:这更多的是一个理论问题,因为我意识到我可以使用如下解决方法:

#!/bin/bash
for file in * ; do 
    cat "$file" | sed 's/^ \+//' | cat -s > "$file"-tmp
    rm "$file"
    mv "$file"-tmp "$file"
done

但这似乎不必要地令人费解。那么为什么“直接”方法如此不可靠呢?

最佳答案

发生不可预测性的原因是管道中的两个阶段(cat "$file"cat -s > "$file")之间存在竞争条件。

第一个尝试打开文件并从中读取,而另一个尝试清空文件。

  • 如果在读取之前已清空,您将得到一个空文件。
  • 如果在清空之前读取它,您将获得一些数据(但文件很快就会被清空,并且结果会被截断,除非它非常短)。

如果您有 GNU sed,则只需执行 sed -i 'expression' *

关于bash - 为什么在管道中读取和写入同一文件会产生不可靠的结果?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20620966/

相关文章:

linux - 哪个命令可以快速搜索一行中的连续模式

linux - IO 重定向到另一个文件

python - 使用 awk 系统将特殊字符输入 python sys.argv

linux - 包含多个命令的 Shell 脚本文件

linux - 有没有办法让 bash 脚本处理使用 write 命令发送给它的消息

c++ - 在 while 循环中使用输入重定向

bash ->& 是什么意思?

database - 备份 postgres 数据库时,系统提示我输入密码

Bash - 仅使用 awk 打印矩阵的某些部分

sed - 使用 sed 提取文件的两个表达式之间的第 n 次或最后一次出现的行 block