文件的头和尾

假设您有一个 txt 文件,同时查看文件的前10行和后10行的命令是什么?

也就是说,如果文件长达200行,那么一次查看1-10行和190-200行。

75053 次浏览

首先是 file.ext 的10行,然后是最后的10行:

cat file.ext | head -10 && cat file.ext | tail -10

文件的最后10行,然后是前10行:

cat file.ext | tail -10 && cat file.ext | head -10

然后,你也可以将输出通过管道输送到其他地方:

(cat file.ext | head -10 && cat file.ext | tail -10 ) | your_program

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

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

ed就是 standard text editor

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

你可以把他们锁在一起,像这样, head fiename_foo && tail filename_foo.如果这还不够,您可以在。配置文件或您使用的任何登录文件:

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

然后,在 shell 提示符 head_and_tail filename_foo中调用它。

你可以简单地:

(head; tail) < file.txt

如果你因为某些原因需要使用管道,那么像这样:

cat file.txt | (head; tail)

注意: 如果 file.txt 中的行数小于 head 的默认行数 + tail 的默认行数,将打印重复的行。

这里的问题是面向流的程序不能预先知道文件的长度(因为如果是真正的流,可能就不会有文件长度)。

tail这样的工具缓冲看到的最后 n 行,等待流的结束,然后打印。

如果您想在单个命令中执行此操作(并让它处理任何偏移量,如果重叠则不要重复行) ,那么您必须模仿我提到的这种行为。

试试这个:

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

为什么不使用 sed完成这个任务?

sed -n -e 1,+9p -e 190,+9p textfile.txt

要处理管道(流)以及文件,请将其添加到.bashrc 或.profile 文件:

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

那么你不仅可以

headtail 10 < file.txt

还有

a.out | headtail 10

(与普通的老式 a.out | (head; tail)不同,当10超过输入的长度时,这仍然会附加虚假的空行。谢谢前面的回答)

注意: headtail 10,不是 headtail -10

对于纯流(例如来自命令的输出) ,您可以使用‘ tee’来分叉流,并将一个流发送到 head,一个流发送到 tail。这需要使用 bash (+/dev/fd/N)的“ > (list)”特性:

( 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;'

我编写了一个简单的 Python 应用程序来完成这个任务: https://gist.github.com/garyvdm/9970522

它处理管道(流)和文件。

借鉴上述思想(经过测试的 bash & zsh)

但用的是化名“帽子”“头和尾巴”

alias hat='(head -5 && echo "^^^------vvv" && tail -5) < '




hat large.sql

基于 J.F. 塞巴斯蒂安的评论:

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

这个解决方案花费了大量的时间,似乎是唯一一个涵盖了所有用例(到目前为止)的解决方案:

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 上设置进度条,确保进度点与 head + tail 分开(如果您想管道化 stdout,这非常方便)
  • 避免由于缓冲(stdbuf)而可能出现的不正确的日志顺序
  • 当总行数小于头尾数时,避免重复输出。
(sed -u 10q; echo ...; tail) < file.txt

只是 (head;tail)主题的另一个变体,但是避免了小文件的初始缓冲区填充问题。

我一直在寻找这个解决方案。我自己也试过使用 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] }' ; }

基于@Samus _ 解释的 给你关于@Aleksandra Zalcman 命令如何工作的内容,当你不能在不计算行数的情况下快速发现尾巴从哪里开始时,这种变化是很方便的。

{ head; echo "####################\n...\n####################"; tail; } < file.txt

或者,如果您开始使用20行以外的内容,行计数甚至可能会有所帮助。

{ head -n 18; tail -n 14; } < file.txt | cat -n

要打印文件的前10行和最后10行,您可以尝试这样做:

Cat < (head-n10 file.txt) < (tail-n10 file.txt) | less

sed -n "1,10p; $(( $(wc -l ${aFile} | grep -oE "^[[:digit:]]+")-9 )),\$p" "${aFile}"

注意 : 文件变量包含文件的 全路径

我想说的是,根据文件的大小,主动阅读其内容可能是不可取的。在这种情况下,我认为一些简单的 shell 脚本就足够了。

下面是我最近如何处理一些非常大的 CSV 文件,我正在分析:

$ for file in *.csv; do echo "### ${file}" && head ${file} && echo ... && tail ${file} && echo; done

这会打印出每个文件的前10行和最后10行,同时还会打印出文件名和前后的省略号。

对于单个大文件,您可以简单地运行以下命令以获得相同的效果:

$ head somefile.csv && echo ... && tail somefile.csv

使用标准输入,但是很简单,并且适用于99% 的用例

头和尾

#!/usr/bin/env bash
COUNT=${1:-10}
IT=$(cat /dev/stdin)
echo "$IT" | head -n$COUNT
echo "..."
echo "$IT" | tail -n$COUNT

例子

$ seq 100 | head_and_tail 4
1
2
3
4
...
97
98
99
100

我做了一些更多的实验,主要是基于这里的建议。工作了一段时间后,我在另一个注释中找到了一些与另一个版本非常相似的内容,但是除了 stdin 之外,我更关注使用多个文件参数进行格式化。

这将很好地包装成一个脚本(暂定为 headtail)并使用 gnu awk。在 macOs 上,这可以通过 brew install gawk安装。

它可以作为参数处理管道内容或文件列表。 给定文件,它打印文件名的头、头 N 行、跳过的行生成器,然后是尾 N 行。如果头部和尾部重叠或将排成一行,它既不包括跳过标记,也不显示任何重复的行。

#!/bin/bash
headtail_awk() {
N=10
gawk -v "n=${N}" -- '\
FNR == 1 && FILENAME != "-" {
printf "\033[036m==> %s <==\033[0m\n", FILENAME;
}
# print head lines
FNR <= n { print }
# store lines in a circular buffer
{ a[FNR % n]=$0 }
# print non-overlapping tail lines from circular buffer.
ENDFILE {
if ( FNR > 2 * n ) {
printf "\033[0;36m>>> %s lines skipped <<<\033[0m\n", FNR - 2 * n
}
for (i=FNR-n+1;i<=FNR;i++) {
if ( i > n) {
print a[i % n]
}
}
}
' "$@"
}
headtail_awk "$@"

我将把 N = 10行窗口的任何 getopts 和/或增强作为练习留给读者。

多个文件的示例输出(n = 3) :

$ headtail -n 3 /usr/share/dict/words /usr/share/dict/propernames
==> /usr/share/dict/words <==
A
a
aa
>>> 235880 lines skipped <<<
zythum
Zyzomys
Zyzzogeton
==> /usr/share/dict/propernames <==
Aaron
Adam
Adlai
>>> 1302 lines skipped <<<
Wolfgang
Woody
Yvonne

这招对我很管用: (head-100) < source. txt > target.txt

(head -100) < source. txt 获取 source. txt 文件的前100行,然后

Txt 将这100行代码压入一个名为 target.txt 的新文件中

起初,我认为这样的方法应该奏效: Head-100 source. txt > target.txt 但是它返回了一个空文件。