cat file.txt > file.txt 导致 file.txt 被清空

2019-06-10 22:01:56 +08:00
 HeiXiaoBai
这个操作为什么会导致这样的结果?
cat file.txt | grep xxx | sed xxx > file.txt 也是一样
7391 次点击
所在节点    Linux
40 条回复
Jirajine
2019-06-10 22:07:13 +08:00
因为搜索之后无匹配内容?
momocraft
2019-06-10 22:07:30 +08:00
因爲寫 shell (the interpreter) 的人不想 (也做不到) 幫你保護文件

熟悉命令的坑是 unix 用戶自己的責任, 詳見 unix 痛恨者指南
vuuv
2019-06-10 22:08:43 +08:00
因为>是 bash 内部的 IO 重定向标记。当前登录的 bash 会先清空(如果是>>则不清空。)了 file.txt 然后才会 fork 出 cat grep sed 等三个进程。并根据管道符的指示把前一个命令的 stdout 重定向到后者的 stdin。最终把 sed 的 stdout 写入 file.txt 。
ericFork
2019-06-10 22:12:23 +08:00
你这个需求还是用 sponge 吧,来自 moreutils 这个包
kwlokip
2019-06-10 22:14:25 +08:00
你这个命令有什么问题?
HeiXiaoBai
2019-06-10 22:15:26 +08:00
@vuuv #3
看起来有点绕
理解起来是,遇到重定向符>,会先清除重定向的目标文件,然后才执行前面的命令,然后前面的命令因为文件已经是空的,输出自然是空的?
vuuv
2019-06-10 22:19:57 +08:00
@HeiXiaoBai #6 是的。你先要求 bash 清空了文件。然后 cat 就读到空文件了。
如果你希望匹配的内容出现在原文件结尾,那么使用>>。追加写入是不会改变已有内容的。
如果你希望只出现匹配的内容,建议换下后面的文件名。
HeiXiaoBai
2019-06-10 22:51:44 +08:00
@vuuv #7
懂了,多谢,之前还以为是先执行前面,到重定向符才清空,所以才觉得奇怪
pkookp8
2019-06-10 22:53:44 +08:00
两个箭头表示 append,一个箭头等于 open
Meltdown
2019-06-10 22:54:33 +08:00
Stackoverflow 上有个一模一样的问题…
Hardrain
2019-06-11 00:05:34 +08:00
如果你想让上一个命令输出到 stdout 的内容 append 到一个文件,用>>
also24
2019-06-11 00:12:35 +08:00
直接回答 >> 的朋友们,请注意看楼主的文件名
ADMlN
2019-06-11 00:23:16 +08:00
清空文件方法 +1
CEBBCAT
2019-06-11 01:19:52 +08:00
搜一下就有,不是个好问题,学习一下如何使用搜索工具吧。
geelaw
2019-06-11 05:33:34 +08:00
cat file.txt > file.txt 肯定会让 file.txt 清空,但对于有多个管道的情况则不是“天然”会导致 file.txt 清空的(除非文档指出了一些实现细节)。

对于 cmd1 arg1 ... argn > out,shell 惟一正常的实现方式是打开 out 之后 exec,因此 out 会在 cmd1 启动之前就清空。

如果是 cmd1 args1 | cmd2 args2 > out,那么 shell 启动 cmd1、cmd2 以及打开 out 的顺序、方式可以有很多种,在文档没有规定如何实现的情况下,这可能和 shell 具体是怎么写的有关,即使确定了 shell 的实现,实际结果也可以取决于一些竞态条件的结果。
yejianmail
2019-06-11 08:54:14 +08:00
这波操作不是一条 sed -i 就搞定的么?
HeiXiaoBai
2019-06-11 08:59:58 +08:00
@yejianmail 不是,我就是单纯的对这个重定向符为什么会这样有疑惑而已
ps1aniuge
2019-06-11 10:42:08 +08:00
问题 :cat file.txt | grep xxx > file.txt 会被清空

我的看法:
1shell 命令不规范,垃圾。幺蛾子。
2 在 powershell 中,用 cat file.txt | grep xxx > file.txt 也会被清空。这是因为 [最大的兼容] 。
3shell 的话,可以使用多条命令,避过这个幺蛾子。如:
a=`cat /tmp/sf.txt |grep power`
echo $a > /tmp/sf.txt



powershell 本身没有这个问题,powershell 用 get-content 打开文件,用 set-content 保存文件。
cat file.txt | grep xxx | set-content file.txt #linux 的 cat

get-content file.txt | grep xxx | set-content file.txt

结论:
shell 命令不规范,powershell 用 [set-content] ,代替 [>] ,治疗了这个不规范。

powershell 不学 [<] ,powershell 不会 [<<] ,powershell 不懂 [EOF] ,却照样 觉得自己 很牛 x。
大家一定要记住, [<] , [>] , [<<<] , [>>] 这些 shell 中的重定向符号,是 shell 中的邪教,把人带坏,
是你成为脚本大师路上的坑。这些坑的唯一作用是“烧你脑”!
msg7086
2019-06-11 10:47:53 +08:00
@ps1aniuge

cat file.txt | grep xxx | tee file.txt
tee 命令用于写入文件,是 1974 年写出来的,Linux 和 Windows 下都有。

用>覆盖文件是因为>优先于程序执行,PowerShell 里也是这样的,这是>的本质所决定的。如果 PowerShell 发明了>,他也会做成这样。如果你不理解为什么>会优先于程序执行,需要学习一下大学操作系统课程中关于文件描述符的部分。
msg7086
2019-06-11 10:56:01 +08:00
@HeiXiaoBai
整串命令的执行过程是:

1. 打开输入(标准输入或者 <)。
2. 打开输出(标准输出或者 > 或者 >>)。
3. 启动程序,并把输入和输出喂给他们。

用操作系统的话来说:

open(输入) 打开输入
open(输出) 打开输出
pipe; pipe; 创建两条管道
fork 出 proc1,proc2,proc3
proc1.stdin = 输入 fd
proc1.stdout = 管道 1 入口
proc2.stdin = 管道 1 出口
proc2.stdout = 管道 2 入口
proc3.stdin = 管道 2 出口
proc3.stdout = 输出 fd
proc1/2/3 分别 exec 执行目标程序。

你的文件就是在第二步打开输出的时候被覆盖的,在三个程序还没启动的时候,输出就已经清空了。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/572624

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX