1 Jirajine 2019-06-10 22:07:13 +08:00 via Android 因为搜索之后无匹配内容? |
![]() | 2 momocraft 2019-06-10 22:07:30 +08:00 ![]() 因 shell (the interpreter) 的人不想 (也做不到) 你保文件 熟悉命令的坑是 unix 用自己的任, unix 痛恨者指南 |
3 vuuv 2019-06-10 22:08:43 +08:00 via Android ![]() 因为>是 bash 内部的 IO 重定向标记。当前登录的 bash 会先清空(如果是>>则不清空。)了 file.txt 然后才会 fork 出 cat grep sed 等三个进程。并根据管道符的指示把前一个命令的 stdout 重定向到后者的 stdin。最终把 sed 的 stdout 写入 file.txt 。 |
![]() | 4 ericFork 2019-06-10 22:12:23 +08:00 你这个需求还是用 sponge 吧,来自 moreutils 这个包 |
5 kwlokip 2019-06-10 22:14:25 +08:00 via Android 你这个命令有什么问题? |
![]() | 6 heixiaobai OP |
7 vuuv 2019-06-10 22:19:57 +08:00 via Android ![]() @HeiXiaoBai #6 是的。你先要求 bash 清空了文件。然后 cat 就读到空文件了。 如果你希望匹配的内容出现在原文件结尾,那么使用>>。追加写入是不会改变已有内容的。 如果你希望只出现匹配的内容,建议换下后面的文件名。 |
![]() | 8 heixiaobai OP @vuuv #7 懂了,多谢,之前还以为是先执行前面,到重定向符才清空,所以才觉得奇怪 |
![]() | 9 pkookp8 2019-06-10 22:53:44 +08:00 via Android 两个箭头表示 append,一个箭头等于 open |
10 Meltdown 2019-06-10 22:54:33 +08:00 via Android ![]() Stackoverflow 上有个一模一样的问题… |
![]() | 11 Hardrain 2019-06-11 00:05:34 +08:00 如果你想让上一个命令输出到 stdout 的内容 append 到一个文件,用>> |
![]() | 12 also24 2019-06-11 00:12:35 +08:00 直接回答 >> 的朋友们,请注意看楼主的文件名 ![]() |
![]() | 13 ADMlN 2019-06-11 00:23:16 +08:00 清空文件方法 +1 |
![]() | 14 CEBBCAT 2019-06-11 01:19:52 +08:00 via Android 搜一下就有,不是个好问题,学习一下如何使用搜索工具吧。 |
![]() | 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 的实现,实际结果也可以取决于一些竞态条件的结果。 |
16 yejianmail 2019-06-11 08:54:14 +08:00 via Android 这波操作不是一条 sed -i 就搞定的么? |
![]() | 17 heixiaobai OP @yejianmail 不是,我就是单纯的对这个重定向符为什么会这样有疑惑而已 ![]() |
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 中的邪教,把人带坏, 是你成为脚本大师路上的坑。这些坑的唯一作用是“烧你脑”! |
![]() | 19 msg7086 2019-06-11 10:47:53 +08:00 ![]() @ps1aniuge cat file.txt | grep xxx | tee file.txt tee 命令用于写入文件,是 1974 年写出来的,Linux 和 Windows 下都有。 用>覆盖文件是因为>优先于程序执行,PowerShell 里也是这样的,这是>的本质所决定的。如果 PowerShell 发明了>,他也会做成这样。如果你不理解为什么>会优先于程序执行,需要学习一下大学操作系统课程中关于文件描述符的部分。 |
![]() | 20 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 执行目标程序。 你的文件就是在第二步打开输出的时候被覆盖的,在三个程序还没启动的时候,输出就已经清空了。 |
21 ps1aniuge 2019-06-11 11:08:32 +08:00 tee 命令用于写入文件-----------这谁告诉你的? tee 在我脑中“是把管道输入,输出到 [管道输出] ,并克隆一份,在标准输出”,即屏幕显示。 tee 一个大文件,,中文件,,,的话,因为有屏幕输出,导致这命令执行结果很慢了。 还会对用户产生 [刷屏] 攻击,捣乱屏幕输出。 结论: 把>换成 set-content,是高杆。 把>换成 tee,是幺蛾子。是从屎窝挪到尿窝。 我的格言: win+bat 界,linux+bash 界,对待 powershell 的态度,就是脚本运维人进步的尺度。 |
![]() | 22 hjq98765 2019-06-11 11:37:21 +08:00 cat file.txt >> file.txt 会提示 input file is output file |
![]() | 25 PTLin 2019-06-11 12:36:18 +08:00 可以用 moreutils 里的 sponge 来解决这个问题,cat file.txt | sponge file.txt ,对实现感兴趣也可以去看看源码。 |
![]() | 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 一下吧,微软员工看到都会监介的 |
![]() | 29 guanzhangzhang 2019-06-11 13:16:56 +08:00 @masker 18 楼这哥们我见过,其他群里也在那吹 powershell |
30 catcalse 2019-06-11 14:49:57 +08:00 直接 sed 完事了。为啥要 cat 下。。。为啥要 grep。这个是伪需求。。。 |
31 newmind 2019-06-11 18:10:34 +08:00 via Android 这难道不是常用的清空文件内容的方法的一种 |
![]() | 32 snoopygao 2019-06-11 20:07:58 +08:00 这命令让我想起了人体蜈蚣 |
![]() | 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 " 可以看到整个过程的。 |
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:文件会被清空 |
![]() | 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 命令还有一个破事:禁止忽略错误。 |
![]() | 37 FrankHB 2019-06-12 00:06:30 +08:00 ok,婊 js 魔怔了,262→334 …… |
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 子命令。 |
39 ps1aniuge 2019-06-12 17:26:20 +08:00 |
40 james122333 2019-06-13 10:01:40 +08:00 pipe 能实现的 导向都能实现 多写就了解了 这需求 shell 完全能实现 据我看到的所有写 pipe 一行流的大型应用都写的很丑 举例:steam 的启动脚本 shell 是能写的优雅好维护的 精随少人在讲而已... |