一些编码实验,(在试图找到编码问题的较短答案时进行),导致了一些有趣的惊喜:
seq 2 | while head -n 1 ; do : ; done
输出(点击 Control-C 否则它会永远浪费 CPU 周期):
1
^C
相同,但使用重定向输入文件而不是管道输入:
seq 2 > two
while head -n 1 ; do : ; done < two
输出(点击Control-C):
1
2
^C
问题:
为什么
while
循环不会像 seq 2 | head -n 1
那样停止?为什么重定向输入比管道输入产生更多输出?
以上代码在最近的
Lubuntu上用
dash
和 bash
进行了测试。 seq
和head
都来自coreutils(版本8.25-2ubuntu2)包。
绕过必须按(Ctrl-C)的方法:
timeout .1 sh -c "seq 2 > two ; while head -n 1 ; do : ; done < two"
1
2
timeout .1 sh -c "seq 2 | while head -n 1 ; do : ; done"
1
head -n 1
,当在 stdin 上给出一个空流时,完全符合其权利和规范,可以立即以成功退出状态退出。
因此:
seq 2 | while head -n 1 ; do : ; done
...可以合法地永远循环,因为
head -n 1
不需要以非零状态退出并因此终止循环。 (只有在“发生错误”时,标准才需要非零退出状态,并且文件行数少于输出请求的行数不被定义为错误)。
确实,这是明确的:
当一个文件包含的行少于number行时,应将其完整复制到标准输出。这应该不是一个错误。
现在,如果你的
head
实现,在它第一次调用之后,(打印第一行的内容),退出时让文件指针在第二行的开头排队,(这是绝对不需要这样做),那么第二个循环实例将读取第二行并发出它。然而,这又是一个实现细节,它取决于编写您的 head
实现的人是否选择了 either:
实施者完全有权根据仅在运行时可用的标准来决定要遵循哪些实施。
现在,假设您的
head
always 尝试一次读取 8kb 块。那么,它怎么能让指针在第二行排队呢? [* - 除了向后查找,一些实现在给定文件时执行,但标准不需要;感谢 Rob Mayhoff 在这里的指点]
这可能会发生如果
seq
的并发调用在第一个read
发生时只写入并刷新了一行.
显然,这是一个对时间非常敏感的情况——竞争条件——并且还取决于未指定的实现细节,(
seq
是否在行之间刷新其输出——因为 seq
未指定为 POSIX 的一部分或任何其他标准,在平台之间是完全不同的)。
接受的答案是正确的。
head
不会为输入返回非零值(即使没有输入)
但我确实发现了一些好奇心
我想出了一个正确停止的方法。
seq 10 | while head -c 4 | ifne -n false; do : ; done;
可悲的是,由于
head
的输出遍历 while
. 的主体,因此您无法对构造做太多事情
我发现的一个用途是每 x 个字节插入一个字符。 (包括尾巴)
/> printf '12345678910' | { while head -c 2 | ifne -n false; do printf 'a'; done; }
/> 12a34a56a78a91a0a
你应该改用
sed 's/.\{4\}/&a/g'
这里有一个稍微有用一点的,它需要 2 个字节的输入“procees”,然后把它放在某个地方:
printf '12345678910' | { while true; do head -c 2 < /dev/stdin | ifne -n false >> file.txt || break; done;
你应该改用
split --filter
另一个非常奇怪的用例,当您尝试在 while 循环中使用
head
调用 /dev/stdin
。
/> printf '12345678910' | { while head -c 2 | ifne -n false; do head -c 3 </dev/stdin | ifne -n false >> every3.txt || break; done > every2.txt; }
/> cat every2.txt
12670
/> cat every3.txt
345891
如您所见,每 2 个字节循环一次,然后每 3 个字节循环一次。
12 345 67 891 0
代替bbe
你可以将它用作某种穷人进度指示器
/> printf '12345678910' | while head -c 2 | ifne -n false; do echo "2 bytes travelled" > /dev/stderr ; done > /dev/null;
2 bytes travelled
2 bytes travelled
2 bytes travelled
2 bytes travelled
2 bytes travelled
2 bytes travelled # imperfect because actually only 1 byte travelled here
代替pv
你能实际上用这个结构做什么......
¯\_(ツ)_/¯