子Shell的IO重定向

12

假设有一个文件"foo.txt",它是从以下内容创建的:

$ seq 1 10 > "foo.txt"

我试图读取文件的第一行和最后一行,我开始通过将文件重定向到子shell来实现,以便命令列表按顺序消耗文件 -- 每个命令从上一个命令离开的地方开始消耗输入,我猜想

$ (head -1; tail -1) < "foo.txt"
1
10

然而,如果我使用一个临时文件描述符尝试相同的操作,输出结果将不同:

$ (head -1; tail -1) < <(seq 1 10)
1

问题:

  1. 我不确定如何命名 <(seq 1 10),但这个临时文件描述符和一个常规的文件描述符("foo.txt")之间有什么区别,导致子shell执行行为不同?

  2. 如何实现将 "foo.txt" 重定向到子shell 的相同行为,但不需要临时文件 - 即不创建实际文件。

1个回答

11

这只是一个缓冲问题。请尝试:

cat foo.txt | ( head -1; tail -1)

你可能会看到相同的结果(head会消耗完整个输入)。很可能是因为使用普通文件和管道时,head的行为不同,并且进程替代(这是<(cmd)语法的名称)创建了一个管道。(在Linux中,来自Gnu coreutils的head尝试使用lseek重定位到输出的最终换行符,但是在管道上lseek会失败。)

另外,请尝试:

(head -1; tail -1) < <(cat /usr/share/dict/words)

在这里,head 只读取了输入数据的第一页面,而 tail 则看到了数据的最后一行。

您看到的行为并没有得到很好的定义,因为没有规定 head 允许读取多少数据的标准。如果将 head 替换为 sh -c 'read line; echo "$line"',则可以在所有情况下获得所需的行为。(或编写自己的工具以提供想要的 head 行为——打印固定数量的行,同时不会读取超过那个数量的行。)


2
+1 非常好的解释。然而,楼主问是否可能在没有任何临时文件的情况下获得这样的行为。可以吗?我的测试没有成功。 - fedorqui
通过将 head 替换为 sh -c "read line; echo "$line"',可以在不使用临时文件的情况下实现所需的行为。 - William Pursell
+1 这正是我一直在寻找的解释!自从你谈到创建自己的东西以来,我一直在考虑写一个自己的 shell 脚本,和几个朋友一起;我们正在考虑一个某种程度上功能齐全的 shell,具有模式匹配和所有最酷的技巧 (: - Rubens

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接