Unix - 文件的头部和尾部

168
假设你有一个txt文件,想要同时查看文件的前10行和后10行,应该使用什么命令?
即使文件有200行,也可以一次性查看第1-10行和190-200行。
命令:`head -n 10 filename && tail -n 10 filename | tac`

你说的 "in one go" 是什么意思? - cnicutar
不要使用@cnicutar的方法,即不要先执行head -10 file命令查看数据,然后再单独执行tail -10 file命令查看数据。 - toop
如果您想要一个真正的工作示例,请查看https://dev59.com/G2oy5IYBdhLWcg3wUcj_#44849814 - sorin
22个回答

262

你可以简单地:

(head; tail) < file.txt

如果出于某种原因需要使用管道,则可以像这样:

cat file.txt | (head; tail)
注意:如果file.txt文件的行数小于head和tail默认值之和,则会打印重复的行。

74
严格来说,这并没有给你原始文件的尾部,而是在head命令消耗了文件前10行后,给出了流的尾部。(将其与对于少于20行的文件使用head < file.txt; tail < file.txt进行比较)。只是需要注意的一个非常小的细节。(但还是值得一加。) - chepner
20
好的。如果你想在头部和尾部之间加上一个间隔:(head;echo;tail) < file.txt - Simon Hibbs
6
好的,我会尽力为您翻译。这是需要翻译的内容:“Curious about why/how this works. Asked it as a new question: https://dev59.com/VGYr5IYBdhLWcg3wYpQg”。 - zellyn
10
实际上,你可能连那么多都看不到。虽然“head”命令只显示其输入的前10行,但不能保证它没有消耗更多的内容以找到第10行的结尾,这会减少输入内容供“less”命令显示。 - chepner
39
抱歉要说,但这个答案只适用于某些情况。seq 100 | (head; tail) 只给我前10个数字。只有在更大的输入量(如seq 2000)时,tail 才会获得一些输入。 - modular
显示剩余12条评论

25

ed是一个标准文本编辑器。

$ echo -e '1+10,$-10d\n%p' | ed -s file.txt

2
如果文件的行数超过或少于200行呢?而且你一开始不知道行数呢? - Paul
@Paul 我已经将 sed 改为 ed - kev

22
(sed -u 10q; echo ...; tail) < file.txt

这仅仅是在(head;tail)主题上的另一种变化,但避免了小文件的初始缓冲填充问题。


这对我来说效果最好,但是对于Mac电脑,我必须brew install gnu-sed才能使其正常工作。 - steveb

21
对于纯流(例如从命令输出),您可以使用'tee'将流分叉并将一个流发送到头部,将另一个流发送到尾部。这需要使用bash的'>(list)'功能(+ / dev / fd / N)。
( COMMAND | tee /dev/fd/3 | head ) 3> >( tail )

或者使用 /dev/fd/N(或 /dev/stderr)以及带有复杂重定向的子shell:

( ( seq 1 100 | tee /dev/fd/2 | head 1>&3 ) 2>&1 | tail ) 3>&1
( ( seq 1 100 | tee /dev/stderr | head 1>&3 ) 2>&1 | tail ) 3>&1

(这两个命令都不能在csh或tcsh中运行。)

要获得更好的控制,可以使用以下perl命令:

COMMAND | perl -e 'my $size = 10; my @buf = (); while (<>) { print if $. <= $size; push(@buf, $_); if ( @buf > $size ) { shift(@buf); } } print "------\n"; print @buf;'

1
+1 对流支持。您可以重复使用 stderr:COMMAND | { tee >(head >&2) | tail; } |& other_commands - jfs
2
顺便提一下,对于大于缓冲区大小的文件(在我的系统上为8K),它会中断。cat >/dev/null可以解决这个问题:COMMAND | { tee >(head >&2; cat >/dev/null) | tail; } |& other_commands. - jfs
我喜欢这个解决方案,但是玩了一会儿后,我注意到在某些情况下尾部会在头部之前运行... headtail 命令之间没有保证的顺序。:\ ... - Jan

7

根据J.F. Sebastian的评论

cat file | { tee >(head >&3; cat >/dev/null) | tail; } 3>&1

这样您就可以在一个管道中以不同的方式处理第一行和其余行,这对于处理CSV数据非常有用:

{ echo N; seq 3;} | { tee >(head -n1 | sed 's/$/*2/' >&3; cat >/dev/null) | tail -n+2 | awk '{print $1*2}'; } 3>&1
N*2
2
4
6

6

为了得出这个解决方案花费了我很多时间,但看起来这是目前唯一可以覆盖所有使用情况的解决方案:

command | tee full.log | stdbuf -i0 -o0 -e0 awk -v offset=${MAX_LINES:-200} \
          '{
               if (NR <= offset) print;
               else {
                   a[NR] = $0;
                   delete a[NR-offset];
                   printf "." > "/dev/stderr"
                   }
           }
           END {
             print "" > "/dev/stderr";
             for(i=NR-offset+1 > offset ? NR-offset+1: offset+1 ;i<=NR;i++)
             { print a[i]}
           }'

功能列表:

  • 实时输出头部信息(显然无法输出尾部信息)
  • 不使用外部文件
  • 进度条:在 MAX_LINES 后,每行一个点,对于长时间运行的任务非常有用。
  • 进度条在 stderr 中显示,确保进度点与头部+尾部分离(如果要将 stdout 管道化非常方便)
  • 避免由于缓冲而导致可能的错误日志记录顺序(stdbuf)
  • 当总行数小于头部 + 尾部时,避免重复输出。

这是这里最好的答案。我将它添加到了我的.bashrc文件中的一个函数中,这样我就可以直接使用管道传递给headtail命令。 - jamesbtate

5

head -10 file.txt; tail -10 file.txt

除此之外,你需要编写自己的程序/脚本。


1
很好,我一直使用catheadtail进行管道传输。很高兴知道可以单独使用它们! - Paul
我该如何将这前10个和后10个数据传输到另一个命令中? - toop
1
@Paul - 使用'your_program'作为wc -l,它返回10而不是20。 - toop
4
不需要生成子shell即可实现:{ head file; tail file; } | prog (大括号内部的空格和尾部的分号是必需的) - glenn jackman
1
哇...两年后,我回答的内容与其他人相似(但时间戳早于他们),却因为某个人选择不解释就被踩了。太好了! - mah
显示剩余2条评论

5
这里的问题是流式程序事先不知道文件的长度(因为如果是真正的流,可能就没有长度)。
像tail这样的工具会缓冲最后看到的n行,并等待流的结束,然后打印。
如果你想用单个命令来做到这一点(并且希望它适用于任何偏移量,并且如果它们重叠不重复行),你将不得不模仿我提到的这种行为。
尝试使用awk:
awk -v offset=10 '{ if (NR <= offset) print; else { a[NR] = $0; delete a[NR-offset] } } END { for (i=NR-offset+1; i<=NR; i++) print a[i] }' yourfile

需要更多的工作,以避免偏移量大于文件时出现问题。 - Samus_
太好了,这可以与管道输出一起使用,不仅限于文件:a.out | awk -v ... - Camille Goudeseune
确实 :) 但这是 awk 的正常行为,大多数命令行程序在没有参数调用时都会使用 stdin。 - Samus_
1
非常接近所需的行为,但似乎对于<10行它会添加额外的换行符。 - sorin

2

我一直在寻找这个解决方案。尝试使用sed自己解决,但是不知道文件/流的长度前提下的问题无法克服。在上面提供的所有选项中,我喜欢Camille Goudeseune的awk解决方案。他指出,他的解决方案在数据集足够小的情况下会留下额外的空行。在这里,我提供了他的解决方案的修改版本,可以去除多余的行。

headtail() { awk -v offset="$1" '{ if (NR <= offset) print; else { a[NR] = $0; delete a[NR-offset] } } END { a_count=0; for (i in a) {a_count++}; for (i=NR-a_count+1; i<=NR; i++) print a[i] }' ; }

2

嗯,你总是可以把它们链接在一起。像这样:head fiename_foo && tail filename_foo。如果这还不够,你可以在你的.profile文件或任何你使用的登录文件中编写一个bash函数:

head_and_tail() {
    head $1 && tail $1
}

然后,您可以从shell提示符中调用它:head_and_tail filename_foo


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