我有这三个重定向 stdin/stdout 的示例,其中只有一个按预期工作。如果有人能向我解释这一点,我将不胜感激。
目标是对 file1 中的内容进行排序,并将更改保存到同一个文件。
sort file1 | tee file1 > /dev/null --------> 它起作用了
sort file1 | tee file1 --------> file1 的内容将被删除
sort file1 | tee file1 > file2 --------> file1 的内容将被删除
PS. tee 将标准输入复制到每个文件,也复制到标准输出。
什么使得第一个例子起作用?
答案1
根据我在 Debian Wheezy 上的测试,所有这三种情况都可能导致两种结果(文件 1 被排序并写回其自身,或者没有任何内容被排序并且没有任何内容被写入文件 1)。
我相信这是正常行为,源自 Linux 处理文件的方式。考虑一下该命令 - sort 命令开始读取 file1 并立即将其输出发送到 tee。Tee 读取输出,将其写回 file1 并将其打印到 /dev/null。如果 sort 足够快以读取整个 file1,tee 将获得排序后的输出。但如果 tee 锁定了文件,它会将其擦除(tee 总是擦除输出文件,除非使用附加选项),这几乎就是您所有 3 种情况中发生的情况。
简而言之,假设有时 sort 读取文件 1 的速度不够快。在这种情况下,tee 会在 sort 读取文件之前删除该文件。
我建议采取以下步骤:
cat file1 | sort > /tmp/sorting.tmp; mv /tmp/sorting.tmp file1
如果你想在标准输出上看到排序后的输出,请这样做:
cat file1 | sort | tee /tmp/sorting.tmp; mv /tmp/sorting.tmp file1
在多处理器系统上让 2 个不同的命令处理 1 个文件并不是一个好主意 - 你永远无法确定哪个命令会先执行。在单线程系统上,行为会有所不同 - 顺序执行。
答案2
我怀疑这种行为是否可预测(当然不会依赖于它)。该tee
命令可能会启动一个新进程,将其输入发送到“其他”目的地。操作系统将“缓冲”输出,直到它到达创建目标文件并将其临时缓冲区写入文件的点。发生这种情况(并覆盖源)的确切时刻可能取决于:
- 文件的大小和缓冲区的可用内存
- 已过时间
- 如果管道的输入
tee
完成
这比以下更深刻bash
:这是程序工作的方式bash
。shell 只是解释您输入的命令,并启动执行命令所需的程序。shell 无法控制每个程序的工作方式,更不用说控制这些程序如何交互。要求一个程序(或一组程序)从输入文件中获取数据,并用同一句话将结果写入同一个输入文件,这是用户的责任。
不要忘记,bash 只是用户命令的解释器:它只是shell
操作系统的一个围绕用户意图转换成系统调用的功能。
而且记录也可以!或者这封邮件,它解决了类似的问题。或者这个StackOverflow 线程。 或者这个 Serverfault 线程。
stdin
请注意,如果您从文件获取输入命令, 则重定向 : 也可能发生这种情况: $ myprog < commandfile
。如果myprog
写入命令文件,则不能保证所有commandfile
的命令都会执行。
一个非常基本的类比将是类似于以下指令列表的东西:
- Execute the instructions step by step
- Dip this instruction list in a bucket of black paint
- Type in the following commands:
find /etc -type f -exec cat '{}' \; | tr -c '.[:digit:]' '\n' \
| grep '^[^.][^.]*\.[^.][^.]*\.[^.][^.]*\.[^.][^.]*$'
我猜你会先复制一份?(命令来自高级 Bash 脚本指南)
答案3
因此,您想保留文件的原始内容,同时将更改附加到文件?
tee 默认覆盖写入,尝试使用 -a 标志将更改附加到文件后。
答案4
sort file1 | tee file1 > tmp && mv file1 original && mv tmp file1
您可以将文件写入占位符,将原始文件重命名为备份,然后将占位符移动到原始文件。