cat file.txt > file.txt 导致 file.txt 被清空 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
Distributions
Ubuntu
Fedora
CentOS
中文资源站
网易开源镜像站
heixiaobai
V2EX    Linux

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

  •  
  •   heixiaobai 2019-06-10 22:01:56 +08:00 7873 次点击
    这是一个创建于 2321 天前的主题,其中的信息可能已经有所发展或是发生改变。
    这个操作为什么会导致这样的结果?
    cat file.txt | grep xxx | sed xxx > file.txt 也是一样
    40 条回复    2019-06-13 10:01:40 +08:00
    Jirajine
        1
    Jirajine  
       2019-06-10 22:07:13 +08:00 via Android
    因为搜索之后无匹配内容?
    momocraft
        2
    momocraft  
       2019-06-10 22:07:30 +08:00   1
    因 shell (the interpreter) 的人不想 (也做不到) 你保文件

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

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

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

    你的文件就是在第二步打开输出的时候被覆盖的,在三个程序还没启动的时候,输出就已经清空了。
    ps1aniuge
        21
    ps1aniuge  
       2019-06-11 11:08:32 +08:00
    tee 命令用于写入文件-----------这谁告诉你的?
    tee 在我脑中“是把管道输入,输出到 [管道输出] ,并克隆一份,在标准输出”,即屏幕显示。

    tee 一个大文件,,中文件,,,的话,因为有屏幕输出,导致这命令执行结果很慢了。
    还会对用户产生 [刷屏] 攻击,捣乱屏幕输出。

    结论:
    把>换成 set-content,是高杆。
    把>换成 tee,是幺蛾子。是从屎窝挪到尿窝。

    我的格言:
    win+bat 界,linux+bash 界,对待 powershell 的态度,就是脚本运维人进步的尺度。
    hjq98765
        22
    hjq98765  
       2019-06-11 11:37:21 +08:00
    cat file.txt >> file.txt

    会提示 input file is output file
    guog
        23
    guog  
       2019-06-11 12:01:59 +08:00
    @ps1aniuge #21 改个文件名就 vans 了。啥,你不想改名,sed -i 啊,整这么多幺蛾子
    guog
        24
    guog  
       2019-06-11 12:04:30 +08:00
    @hjq98765 #22 mac 上死循环,无限写文件
    PTLin
        25
    PTLin  
       2019-06-11 12:36:18 +08:00
    可以用 moreutils 里的 sponge 来解决这个问题,cat file.txt | sponge file.txt ,对实现感兴趣也可以去看看源码。
    masker
        26
    masker  
       2019-06-11 13:12:37 +08:00 via Android
    @ps1aniuge 求求你,不要来这里当布道者了
    masker
        27
    masker  
       2019-06-11 13:14:06 +08:00 via Android
    @livid #18 这种贬低他人抬高自己的布道者是否应该封禁
    momocraft
        28
    momocraft  
       2019-06-11 13:15:12 +08:00
    The tee utility copies standard input to standard output, making a copy in zero or more files. The output is unbuffered.

    求求你 RTFM 一下吧,微软员工看到都会监介的
    guanzhangzhang
        29
    guanzhangzhang  
       2019-06-11 13:16:56 +08:00
    @masker 18 楼这哥们我见过,其他群里也在那吹 powershell
    catcalse
        30
    catcalse  
       2019-06-11 14:49:57 +08:00
    直接 sed 完事了。为啥要 cat 下。。。为啥要 grep。这个是伪需求。。。
    newmind
        31
    newmind  
       2019-06-11 18:10:34 +08:00 via Android
    这难道不是常用的清空文件内容的方法的一种
    snoopygao
        32
    snoopygao  
       2019-06-11 20:07:58 +08:00
    这命令让我想起了人体蜈蚣
    msg7086
        33
    msg7086  
       2019-06-11 22:57:00 +08:00
    @guog @masker 之前我还在犹豫要不要 block,现在发现无需犹豫了~ 顺便 @Livid 一下。
    scriptB0y
        34
    scriptB0y  
       2019-06-11 23:42:59 +08:00
    @msg7086 说的这个过程,其实楼主可以用 strace 自己验证一下

    strace -f sh -c "cat file.txt | grep xxx | sed xxx > file.txt " 可以看到整个过程的。
    siteshen
        35
    siteshen  
       2019-06-11 23:57:01 +08:00
    本来用例 1 想质疑 #3 @vuuv 的答案,然而重读一遍后,又用例 2 推翻了我的质疑。

    cat hello.txt | grep a | (sleep 1; cat > hello.txt) # 例 1:文件不会被清空
    cat hello.txt | (sleep 1; grep a) | cat > hello.txt # 例 2:文件会被清空
    FrankHB
        36
    FrankHB  
       2019-06-12 00:05:46 +08:00
    @ps1aniuge
    你似乎不知道啥叫“规范”。
    POSIX shell 是屑,bash 有 private extensions,不妨碍 POSIX 标准化了带有 I/O redirection 的 shell command language。
    相比之下,ps1 的设计好上那么一丢丢,也改不掉 M$的写 spec 落后的现状。ECMA-262 还落后 VC#几年呢……你指望 ps1 能规范就猴年马月了。
    另外,所谓屏幕输出也是你自以为是的笑话,尽管偶然符合事实。tee 命令操作的是标准输入和标准输出。在 UNIX 的万物皆文件的邪教下,对应文件还真没错。下面那个叫你 RTFM 的咣的还真是时候。
    另外你漏了 POSIX.1 标准化的 tee 命令还有一个破事:禁止忽略错误。
    FrankHB
        37
    FrankHB  
       2019-06-12 00:06:30 +08:00
    ok,婊 js 魔怔了,262→334 ……
    vuuv
        38
    vuuv  
       2019-06-12 14:54:36 +08:00 via Android
    @siteshen #35 文件真的没有被清空吗?看下文件的修改时间?
    你写的的 hello.txt 是不是每行都恰好包含字母 a 呢?加一行不含字母 a 的内容试试?

    例 1 里的圆括号“()”标记会 fork 一个 shell (暂称为子 shell )来执行。于是命令等价为这样的:
    cat hello.txt | grep a | bash -c "sleep 1; cat > hello.txt"

    如果没有 sleep 1,那么会立刻在子 shell 里发生文件清空。不过此时 cat 和子 shell 是同时 fork 的,而且子 shell 启动后的初始化及对命令的语法解析会花费一些时间(也就几十毫秒而已)。
    如果 hello.txt 文件较小,等到子 shell 开始奉命清空文件时,cat 是有充足的时间读到文件全部内容的。如果文件超出了缓冲区大小(缓冲区默认是 4k,不过程序可以设置其他大小,内核也可能会多预读点内容。),就不保证 cat 能读到正确的内容了。

    所以一些软件系统会设计为“对关键文件的修改加锁”,就是为了防止多个进程同时修改某个文件。
    典型的代表就是 yum 的 install 子命令。
    ps1aniuge
        39
    ps1aniuge  
       2019-06-12 17:26:20 +08:00
    @masker
    v2 是高智商、程序员的技术社区。
    而你这智商,明显差强任意。拉低了社区平均值。

    什么贬低他人 ? “他人”是指谁? 技术和人你都分不清楚么?
    james122333
        40
    james122333  
       2019-06-13 10:01:40 +08:00
    pipe 能实现的 导向都能实现 多写就了解了
    这需求 shell 完全能实现 据我看到的所有写 pipe 一行流的大型应用都写的很丑 举例:steam 的启动脚本
    shell 是能写的优雅好维护的 精随少人在讲而已...
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     1273 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 27ms UTC 17:22 PVG 01:22 LAX 10:22 JFK 13:22
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86