我想在不使用管道的情况下 grep 查找文件中最后 n 行的单词。
grep <string> filename
允许搜索文件名中的字符串。但是,我想在文件的最后 N 行中搜索字符串。有什么命令可以在不使用管道的情况下搜索它吗?
答案1
如果您的 shell 支持它(zsh
、bash
、 的某些实现ksh
),您可以使用流程替代
grep <pattern> <(tail -n5 yourfile.txt)
其中 -n5 表示获取最后五行。
相似地,
grep <pattern> <(head -n5 yourfile.txt)
将搜索 yourfile.txt 的前 5 行。
解释
简单来说,替换的进程伪装成一个文件,这正是 grep 所期望的。进程替换的优点之一是您可以将多个命令的输出作为其他命令的输入,如diff
本例所示。
diff -y <(brew leaves) <(brew list)
这去掉了管道 (
|
) 字符,但每次替换实际上都是创建管道1 .
1请注意,ksh93
至少在 Linux 上,|
不是使用管道但使用套接字对流程替代open
确实使用管道(因为套接字不可能):
$ ksh93 -c 'readlink <(:)' 管道:[620224] $ ksh93 -c ': | readlink /proc/self/fd/0' 插座:[621301]
答案2
n=$some_num
{ head -n"$(($(wc -l <in)-n))" >/dev/null
grep 'match your string'
} <in
不幸的是,这需要通过 w/ 完全读取文件wc
以获得行数,因为否则不清楚文件中有多少行或有多大$n
。除此之外,这应该是一个非常高性能的解决方案,只要<in
是常规的,lseek()
有能力的文件。
所以首先我们得到行数并$n
从中减去。head
从 stdin 读取那么多行并将结果写入/dev/null
.之后剩下的就是$n
stdin 上的输入行数以及您grep
和您的模式。
从技术上讲,这确实作弊 - 那里是命令中的管道替换为wc
.我希望你能忽略这一点。
顺便说一句,另一种方法可以如下所示:
{ grep "-m$n" 'some pattern near yours' >/dev/null
grep 'your pattern'
} <in
...与 GNU grep
。如果您可以grep
$n
出现另一种模式,使您处于目标模式的附近,那么您可能真的不需要管道就能做到这一点。
我尝试坚持使用 w/ grep
,但sed
无论如何这里有一个解决方案。下面的管道仅用于输入 - 除了grep
在前面添加行号之外根本不涉及它们,以便您可以看到它们是哪些数字。所有这些仅适用于示例案例。您可以将sed
脚本单独与任何类型的命名文件或标准输入一起使用,$pat
并$n
进行适当的设置,它将起作用。
实际上我只是重写了这个,因为我不喜欢无法锚定比赛。这是一点较慢 - 不明显,并且它仍然非常快,但是对于每个缓冲的尾行,它会修剪所有尾随模式空间并隔离缓冲区中的第一行。通过这种方式,所有正常的锚表达式都可以按预期工作。
pat=man n=40
man man |
grep -n ''|
sed -e:B -e'${/^\n/D' \
-eh -e's/\n.*//' \
-e"/$pat/p;x" \
-e\} -e'$D;N;$bB' \
-e"$n,$ D;bB"
648: /etc/man_db.conf
649: man-db configuration file.
651: /usr/share/man
652: A global manual page hierarchy.
654: /usr/share/man/index.(bt|db|dir|pag)
657: /var/cache/man/index.(bt|db|dir|pag)
661: apropos(1), groff(1), less(1), manpath(1), nroff(1), troff(1), whatis(1),
662: zsoelim(1), setlocale(3), manpath(5), ascii(7), latin1(7), man(7), cat-
663: man(8), mandb(8), the man-db package manual, FSSTND
680: developing and maintaining man-db.
这是另一个例子,但是在一个文件上:
pat=. n=15
seq 100 >nums
sed -e:B -e'${/^\n/D' \
-eh -e's/\n.*//' \
-e"/$pat/p;x" \
-e\} -e'$D;N;$bB' \
<nums -e"$n,$ D;bB"
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
答案3
为什么要避免使用管道?
如果您确实想避免使用管道,那么您将必须运行两个命令:
tail -N filename > filename.tmp
grep "string" filename.tmp
(when N is the last number of lines)
答案4
您可以通过awk
一点帮助来做到这一点:
$ N=8
$ awk -v start_line="$(( $(wc -l < alphabet) - N + 1 ))" 'NR>=start_line && /e/' alphabet
sierra
whiskey
yankee
$
e
查找包含在最后 8 行中的 所有行拼音字母表。这样做的缺点是它会读取整个输入文件两次。