为什么构造 1 和 2 按预期工作,而构造 3 只输出行数?
输出文件的第一行和最后一行:
$ (head -n 1; tail -n 1) < файл输出文件的第一行和行数:
$ (head -n 1; wc -l) < файл(更新 1:事实上,事实证明它的工作方式并不像这样:它
wc显示的数字比文件长度小一——这一行被“吃掉了”head)。输出文件的行数,然后是第一行(不起作用 - 只显示行数):
$ (wc -l; head -n 1) < файл
update2:关于来自gnu/coreutils的头程序的“特殊实现” ,例如,可以争辩说“busybox”实现的行为相同。此命令输出与第 1 点相同:
$ (busybox head -n 1; tail -n 1) < файл
而且一般来说,几乎不可能“注销一切”来实现head 。因为结构的行为:
$ cat файл | (head -n 1; tail -n 1)
$ (head -n 1; tail -n 1) < <(cat файл)
与第一个示例中给出的不匹配:仅显示文件的第一行。
程序版本(尽管这很可能几乎不重要):
$ head --version
head (GNU coreutils) 8.26
Copyright (C) 2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Written by David MacKenzie and Jim Meyering.
$ wc --version
wc (GNU coreutils) 8.26
Copyright (C) 2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Written by Paul Rubin and David MacKenzie.
$ bash --version
GNU bash, version 4.4.12(1)-release (x86_64-pc-linux-gnu)
Copyright (C) 2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
ps,知道这样的构造 ( (команда1; команда2) < файл) 记录在哪里会很有趣。
要回答的正确问题是:
为什么构造
(head -n 1; head -n 1) < файл(下面称为“源命令”)在发出文件的前两行时完全起作用?通过正确回答它,将有可能回答其余的问题。
看了头程序的踪迹后得到了答案。测试文件:
在gnu/linux下运行(这里原始命令产生两行):
在solaris下运行(这里原始命令也产生两行):
在freebsd下运行(这里原来的命令只打印第一行):
只剩下基本的输出线。他们表明,所有三个实现中的head程序都从stdin读取所有 33 个字节(由 shell 与真实文件关联),然后将前 11个字节写入stdout。
但是在gnu/linux和solaris的情况下,读取后,指针被“倒带”(lseek/llseek)到输入文件中的第 11 位(到第二行的开头),而在freebsd中则不会发生这种情况。
从这里可以清楚地看出为什么看似相同的结构:
在所有三个测试系统中的工作方式都相同(仅显示文件的第一行):毕竟,只有在真实文件中才能“倒带”,而且当它不是文件,而是通过“ pipe” ( pipe ,
|),则无法重新排列指针。构造本身
(команда1; команда2) < файл并没有什么特别之处:启动了一个子shell,它被赋予了文件。在subshell中,首先执行command1 ,然后执行command2。它们都依次从同一个标准输入中读取。在简化版本中,您可以这样想:
head读取第一行,然后停止并打印,读取其余部分tail。但是AFAIK给定的行为没有标准化。同样,它
head读取第一行,其余的都转到wc.wc打印输出的行数减一。wc阅读所有内容,但head'一无所获。所有这些都是非常肮脏的东西,而且第一个命令起作用的事实仅与 GNU 实现相关
head。你不能依赖它。您可以正确地将输出重定向到两个不同的进程,
bash例如:但是没有定义输出
head到终端的顺序。tail在符合基本 POSIX 的外壳程序 AFAIK 中,这只能使用命名管道来完成。
更新
各种shell的测试结果:
测试文件:
测试的外壳:* GNU/Linux:bash、dash、bussybox sh * FreeBSD 8.0:csh、bash
测试命令:
<shell> -c 'cat /tmp/file | (head -n 1; tail -n 1)'<shell> -c 'cat /tmp/file | (head -n 1; head -n 1)'在所有贝壳上给出相同的结果:
$ <shell> -c '(head -n 1; tail -n 1) </tmp/file'$ <shell> -c '(head -n 1; head -n 1) </tmp/file'GNU/Linux(所有外壳):
FreeBSD 8(所有外壳):
<файл在 shell(POSIX,bash)中将文件重定向到标准输入(从 stdin (fd 0) 读取的命令将获取文件的内容)。wc -l在第 3 步中读取整个输入,因此没有head什么可分享的了。该实用程序的文档(POSIX和Gnu )不保证它
head -n 1不会消耗所有输入(允许步骤 1 和 2 中的命令工作)。可以合理地预期不会完成额外的工作,特别是如果它不会使实施复杂化。例如FreeBSD:headhead.cfgetln()使用 stdio 缓冲区,因此它可以从标准输入中读取更多内容。这解释了为什么(head -n 1; head -n 1) <файл它在 FreeBSD 上不起作用。tail可以忽略文件中的当前位置@Fat-Zer 想通了。因此,它(head -n 1; tail -n 1) <file也可以在 FreeBSD 上运行。GNU head.c也可以读取更多
BUFSIZ的内容(读取一行时,尝试将位置设置在行尾)。head 实用程序本身的文档不保证此行为。由于管道不可搜索,这就解释了为什么 c 选项
cat file|不起作用。根据POSIX,需要标准实用程序(例如 head、sed、tail)来设置可搜索输入的文件位置(这是 POSIX 指定的分组命令中的尾部行为吗?):
也就是说,在 POSIX 上,for 的行为是有
(head -n 1; tail -n 1) <file保证的。( compound-list )命令在子shell中执行(比看起来更常见,例如,管道中的每个命令都可以在子shell中执行(在单独的进程中)。如果不需要单独的过程,则{ compound-list ; }可以使用该构造(重定向仍然有效)。如果不清楚重定向的工作原理以及作为官方文档的补充 - POSIX、bash ,您可以查看带图表的介绍手册。
因为 shell 做了 1
open()次,然后 2 次dup2()。因此,在命令中,它共享与返回stdin的文件描述符关联的共享读写指针。open有点出乎意料的非常整洁的行为
head(tail在这方面我真的很震惊(请参阅@alexanderbarakin,@Fat-Zer 和 @jfs 的答案和评论中的纹理))将指针移动到与字节对应的逻辑正确位置它们实际上会在顺序读取文件时进行处理。