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 · 7563 次点击
    这是一个创建于 2025 天前的主题,其中的信息可能已经有所发展或是发生改变。
    这个操作为什么会导致这样的结果?
    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   ·   实用小工具   ·   3127 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 13:30 · PVG 21:30 · LAX 05:30 · JFK 08:30
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.