我有一个包含多行的文件,每行在开头都有一个时间戳,例如
[Thread-3] (21/09/12 06:17:38:672) logged message from code.....
因此,我经常从这个日志文件中检查两件事。
- 前几行具有全局条件,并且还给出了开始时间。
- 最后几行包含退出状态和其他一些信息。
是否有任何快速方便的单个命令可以让我仅显示文件的第一行和最后几行?
答案1
@rush 关于使用 head + tail 对于大文件更有效是正确的,但对于小文件(< 20 行),某些行可能会输出两次。
{ head; tail;} < /path/to/file
会同样有效,但不会出现上述问题。
答案2
您可以使用sed
或awk
通过一个命令来完成它。然而,你会很快失去速度,因为无论如何sed
都awk
需要运行整个文件。从速度的角度来看,最好创建一个函数或每次都组合tail
+ head
。如果输入是管道,这确实有不起作用的缺点,但是如果您的 shell 支持它,您可以使用进程替换(请参见下面的示例)。
first_last () {
head -n 10 -- "$1"
tail -n 10 -- "$1"
}
然后将其启动为
first_last "/path/to/file_to_process"
继续进行进程替换(仅限 bash、zsh、ksh 等 shell):
first_last <( command )
附注您甚至可以添加一个grep
来检查您的“全局条件”是否存在。
答案3
该{ head; tail; }
解决方案不适用于管道(或套接字或任何其他不可查找的文件),因为head
在按块读取时可能会消耗太多数据,并且无法在管道上查找,可能会使光标在文件内超出其tail
含义选择。
因此,您可以使用一种像 shell 一样一次读取一个字符的工具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
或者在 awk 中实现,tail
例如:
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
的标准输出被输入到管道tail
而不是控制台,这根本不是我们想要的。