Linux 中的管道/重定向究竟如何工作?

Linux 中的管道/重定向究竟如何工作?

我有这三个重定向 stdin/stdout 的示例,其中只有一个按预期工作。如果有人能向我解释这一点,我将不胜感激。

目标是对 file1 中的内容进行排序,并将更改保存到同一个文件。

  1. sort file1 | tee file1 > /dev/null --------> 它起作用了

  2. sort file1 | tee file1 --------> file1 的内容将被删除

  3. 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

您可以将文件写入占位符,将原始文件重命名为备份,然后将占位符移动到原始文件。

相关内容