显示文件的前几行和后几​​行的命令


23

我有一个包含很多行的文件,每行的开头都有一个时间戳,例如

[Thread-3] (21/09/12 06:17:38:672) logged message from code.....

因此,我经常检查此日志文件中的2件事。

  1. 还给出了具有全局条件和开始时间的前几行。
  2. 最后几行具有退出状态以及其他一些信息。

是否有任何方便的单个命令可以让我仅显示文件的前几行和后几​​行?


2
全球情况如何,head and tail对您不起作用?
雏菊2012年

那是我的日志文件的一部分。我试图变得详尽。您可以忽略它。
mtk 2012年

您的解决方案对我来说很好。如果您想要更多便利,请将其放入shell函数中(甚至可以使用别名)。
vonbrand

@vonbrand问题是我不知道N
Bernhard

@Bernhard,我不是sed(1)专家,但是有一些方法可以将东西藏起来供以后使用。或许值得一看。OTOH,如果经常使用的话,我可能会编写一个Perl(或其他)脚本来执行此操作,因为我对此更加熟悉。
vonbrand

Answers:


12

您可以通过一个命令使用sedawk使其。但是,您会因为速度过快而导致松动,sed并且awk无论如何都需要遍历整个文件。从速度的角度来看,最好使函数或每次都组合tail+ head。如果输入是管道,这样做的缺点是不起作用,但是,如果您的外壳支持它,则可以使用过程替换(请参见下面的示例)。

first_last () {
    head -n 10 -- "$1"
    tail -n 10 -- "$1"
}

然后以

first_last "/path/to/file_to_process"

进行进程替换(仅bash,zsh,ksh类似于shell):

first_last <( command )

ps。您甚至可以添加一个grep以检查您的“全局条件”是否存在。


-n 10是默认值,不是吗?
l0b0

@ l0b0是,它是默认值。-n 10这里没有必要。

20

@rush对于大文件使用头尾比较有效,但是对于小文件(<20行),某些行可能会输出两次。

{ head; tail;} < /path/to/file

同样有效,但不会遇到上述问题。


与rush解决方案相反,这在POSIX shell中不起作用。
Marco Marco

2
@Marco Huh?这里仅使用POSIX构造。您怎么看错了?
吉尔斯(Gillles)“所以-别再邪恶了”

2
@吉尔斯我错过了空间:{head; tail;} < file在zsh中工作,但在sh中失败。{ head; tail;} < file一直有效。对不起,噪音。
Marco Marco

@Marco,如果有问题,那就是head,而不是外壳。POSIX要求head将光标留在文件中刚好在常规文件的那10行之后。非POSIX head实现可能会出现问题(在这种情况下,以前的GNU head的旧版本一直不符合要求,但我们谈论的是几十年),或者该文件不可搜索(如命名管道或套接字,但随后会出现问题)。其他解决方案将具有相同的问题)。
2013年

1
@FCTW,sudo sh -c '{ head; tail;} < /path/to/file'
斯特凡Chazelas

9

{ head; tail; }解决方案无法在管道(或套接字或任何其他不可搜索的文件)上使用,因为它head可能会消耗过多的数据,因为它被块读取,并且无法在管道上找回,可能会将光标留在文件内,超出了tail预期范围选择。

因此,您可以使用一种像外壳程序那样一次读取一个字符的工具read(此处使用以标题行和结尾行的数量为参数的函数)。

head_tail() {
  n=0
  while [ "$n" -lt "$1" ]; do
    IFS= read -r line || { printf %s "$line"; break; }
    printf '%s\n' "$line"
    n=$(($n + 1))
  done
  tail -n "${2-$1}"
}
seq 100 | head_tail 5 10
seq 20 | head_tail 5

tail在awk中实现为:

head_tail() {
  awk -v h="$1" -v t="${2-$1}" '
    {l[NR%t]=$0}
    NR<=h
    END{
      n=NR-t+1
      if(n <= h) n = h+1
      for (;n<=NR;n++) print l[n%t]
    }'
}

sed

head_tail() {
  sed -e "1,${1}b" -e :1 -e "$(($1+${2-$1})),\$!{N;b1" -e '}' -e 'N;D'
}

(尽管要注意,某些sed实现对其模式空间的大小限制很小,所以对于较大的尾线数量而言,这样做会失败)。


4

使用bash进程替换,您可以执行以下操作:

make_some_output | tee >(tail -n 2) >(head -n 2; cat >/dev/null) >/dev/null

请注意,尽管对于长度超过8kB的文件,它们不一定会按顺序排列,但很有可能会如此。此8kB截止值是读取缓冲区的典型大小,并且与| {head; tail;}小文件不起作用的原因有关。

cat >/dev/null保持必要head的管道活着。否则,tee将提早退出,并且当您从中获取输出时tail,它将来自输入中间的某个地方,而不是末尾。

最后,为什么>/dev/null而不是搬到tail另一个地方|呢?在以下情况下:

make_some_output | tee >(head -n 2; cat >/dev/null) | tail -n 2  # doesn't work

head的stdout被馈送到管道tail而不是控制台,这根本不是我们想要的。


当头尾完成所需的输出时,它们将关闭标准输入并退出。那就是SIGPIPE的来源。通常这是一件好事,因为他们会丢弃其余的输出,因此没有必要让管道的​​另一端继续花费时间来生成它。
derobert

是什么使订单得以维持?它可能适用于大文件,因为tail它必须工作更长的时间,但是我希望(确实会看到)短输入失败大约一半的时间。
吉尔(Gilles)'所以

tee >(head) >(tail)出于同样的原因>(...),您将获得SIGPIPE (顺便说一下,这也是zsh和bash现在都支持的ksh功能)也使用管道。您可以这样做,... | (trap '' PIPE; tee >(head) >(tail) > /dev/null)但仍会看到来自的管道破裂错误消息tee
斯特凡Chazelas

在我的系统上(bash 4.2.37,coreutils 8.13),tail是被SIGPIPE杀死的那个,不是tee,并且tail没有写入管道。所以它一定是来自kill(),对吧?这仅在使用|语法时发生。 strace说那tee不是在打电话kill()...也许是bash吗?
詹德

1
@Jander,尝试饲养超过8K像seq 100000 | tee >(head -n1) >(tail -n1) > /dev/null
斯特凡Chazelas

3

使用ed(它将整个文件读入RAM):

# cf. http://wiki.bash-hackers.org/howto/edit-ed
printf '%s\n' 'H' '1,10p' '$-10,$p' 'q' | ed -s file

简短一点:ed -s file <<< $'11,$-10d\n,p\nq\n'
don_crissti 2014年

2

Stephane在函数中的第一个解决方案,以便您可以使用参数(可在任何类似Bourne或POSIX的shell中使用):

head_tail() {
    head "$@";
    tail "$@";
}

现在您可以执行以下操作:

head_tail -n 5 < /path/to/file

当然,这假设您只查看一个文件,并且像Stephane的解决方案一样(可靠地)仅对常规(可搜索)文件起作用。


2

使用GNU 的-u--unbuffered)选项sed,您可以sed -u 2q用作以下内容的无缓冲替代方法head -n2

$ seq 100|(sed -u 2q;tail -n2)
1
2
99
100

(head -n2;tail -n2)当最后几行是消耗的输入块的一部分时,失败head

$ seq 1000|(head -n2;tail -n2)
1
2
999
1000
$ seq 100|(head -n2;tail -n2)
1
2

这应该是最佳答案!奇迹般有效!
本·乌斯曼

1

今天,我遇到了类似这样的情况,我只需要最后一行和流前面的几行,然后得出以下内容。

sed -n -e '1{h}' -e '2,3{H}' -e '${H;x;p}'

我将其读取为:用第一行的内容初始化容纳空间,在容纳空间中添加第2-3行,在EOF处将最后一行添加到容纳空间中,交换保留和图案空间,并打印图案空间。

也许sed比我拥有更多-fu的人可以弄清楚如何将其概括以打印此问题中指示的流的最后行,但是我不需要它并且找不到基于$地址的简单数学方法进入sed或可能通过管理容纳空间,以便在EOF到达时仅保留最后几行。


1

如果安装了Perl,则可以尝试使用Perl:

perl -e '@_ = <>; @_=@_[0, -3..-1]; print @_'

这将适用于大多数文件,但在处理之前会将整个文件读入内存。如果您不熟悉Perl切片,则方括号中的“ 0”表示“采用第一行”,而“ -3 ...- 1”表示“采用最后三行”。您可以根据自己的需要定制它们。如果您需要处理非常大的文件(“大”文件可能取决于您的RAM或交换大小),则可能需要进行以下操作:

perl -e 'while($_=<>){@_=(@_,$_)[0,-3..-1]}; print @_'

它可能会稍慢一些,因为它会在每次迭代时进行切片,但它与文件大小无关。

这两个命令都应在管道和常规文件中均有效。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.