使用 'head' 和 'tail' 从 cat 输出中读取 'first and last line' 时出现奇怪的结果

Strange result while perform reading 'first and last line' from cat output with 'head' and 'tail'

Bash 版本 4.4.7.

来自 tutorial,获取文件的第一行和最后一行:

cat txt_file | (head -n1 && tail -n1)

但是,对于大文件(我不知道这会有多大,但文件有大约几千行)这个命令运行良好,但对于小文件,例如:

11111111
22222222
33333333
44444444

about命令的输出只有第一行:

11111111

另一个命令,使用 awk,适用于两个文件:

awk 'NR==1; END{print}'

你现在的"question"实际上并不是一个问题,它只是一个观察。然而,要解释你的观察。考虑输出之间的差异:

$ seq 10 | (head -1 && tail -1)
1

$ seq 1000 | (head -1 && tail -1)
1
1000

这里发生了什么?我们的管道工作如下:

  • 将行(在本例中为数字,但与您的 cat 示例没有区别)发送到标准输出;
  • 读取标准输出我们有:

    • 首先,一个head ...它会打印第一行然后结束;
    • 接下来,一个 tail ... 它将在 之后 开始 运行 并打印最后一行。

但是,默认情况下,head不是逐行读取文件,甚至是逐字符读取直到找到换行符,而是读取文件文件块(缓冲读取)。例如,该块可能是 2048 字节。

所以我们的管道真的是:

  • 将行(在本例中为数字,但与您的 cat 示例没有区别)发送到标准输出;
  • 读取标准输出我们有:

    • 首先,一个head ...它将从标准输入读取前2kb,打印第一行然后结束;
    • 接下来,tail ...它将读取第一个 2k 之后的剩余数据 ,因为它从未看到它

如果您的目标是只生成一次第一个命令(您的 cat)的输出,那么您可以使用 tee,可能是这样的:

$ seq 10 | tee >(tail -1) | head -2

另请注意,在 linux 上,您可以更改第一个命令的缓冲,例如:

$ stdbuf -oL seq 10 | (head -1 && tail -1)

但如果您的命令摆弄其流(请参阅 stdbuf),这将不起作用

也尝试以下操作: sed解决方案:

sed -n '1p;$p' <(seq 1000)

perl解决方案:

seq 100 |  perl -ne 'print if 1..1 or eof'

bash 只有尾巴的解决方案:

seq 100 | { IFS= read -r line; echo "$line"; tail -1; }