[英]unix - head AND tail of file
假设你有一个 txt 文件,同时查看文件的前 10 行和后 10 行的命令是什么?
即如果文件长 200 行,则在一个 go 中查看第 1-10 行和 190-200 行。
您可以简单地:
(head; tail) < file.txt
如果您出于某种原因需要使用管道,则如下所示:
cat file.txt | (head; tail)
注意:如果 file.txt 中的行数小于默认的头部行数+默认的尾部行数,将打印重复的行。
ed
是standard text editor
$ echo -e '1+10,$-10d\n%p' | ed -s file.txt
对于纯流(例如命令的输出),您可以使用“tee”来分叉流并将一个流发送到头部,一个流发送到尾部。 这需要使用 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;'
(sed -u 10q; echo ...; tail) < file.txt
只是(head;tail)
主题的另一个变体,但避免了小文件的初始缓冲区填充问题。
head -10 file.txt; tail -10 file.txt
除此之外,您需要编写自己的程序/脚本。
基于JF 塞巴斯蒂安的评论:
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]}
}'
功能列表:
这里的问题是面向流的程序事先不知道文件的长度(因为可能没有一个,如果它是一个真正的流)。
像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
我一直在寻找这个解决方案。 我自己用 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] }' ; }
我写了一个简单的 python 应用程序来做到这一点: https : //gist.github.com/garyvdm/9970522
它处理管道(流)和文件。
借鉴上面的想法(经过测试的 bash 和 zsh)
但使用别名“帽子”头部和尾部
alias hat='(head -5 && echo "^^^------vvv" && tail -5) < '
hat large.sql
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 fiename_foo && tail filename_foo
。 如果这还不够,您可以在 .profile 文件或您使用的任何登录文件中编写自己的 bash 函数:
head_and_tail() {
head $1 && tail $1
}
并且,稍后从您的 shell 提示符调用它: head_and_tail filename_foo
。
要处理管道(流)和文件,请将其添加到 .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
(当 10 超过输入的长度时,这仍然会附加虚假的空行,这与普通的a.out | (head; tail)
。谢谢以前的回答者。)
注意: headtail 10
,而不是headtail -10
。
建立在@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}"
注意: 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
我主要根据此处的建议做了一些更多的实验。 经过一番工作后,我在另一条评论中提出了与另一个版本非常相似的内容,但除了标准输入之外,我更关注使用多个文件参数进行格式化。
这很好地包装成一个脚本(暂定headtail
)并使用 gnu awk。在 macOs 上可以通过brew install gawk
。
它可以处理管道内容或文件列表,如 arguments。给定文件,它打印文件名的 header,开头 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 行 window 的任何 getopts 和/或增强作为读者的练习。
多个文件的样本 output(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 行,然后
taget.txt 将 100 行推送到一个名为 target.txt 的新文件中
最初我认为这样的事情应该有效: head -100 source.txt > target.txt 但它返回了一个空文件。
为什么不使用sed
来完成这项任务?
sed -n -e 1,+9p -e 190,+9p textfile.txt
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.