我想从文件file.txt
中读取所有索引n
低于与给定正则表达式regex
匹配的行的行。例如文件
hello my friend
foo
_bar_
I love this bar
poof
kouki
splash in the water
bar
如果regex=bar
和n=2
,那么我们想要阅读
hello my friend
foo
kouki
我找到了解决这个问题的麻烦的单线
sed -n `grep -n bar file.txt | awk -F ":" '{print ($1 - 2)}' | tr '\n' 'X'
| sed 's+X+p;+g' | sed 's/.$//'` < file.txt
是否有更好(更快,更易读)的解决方案?
(我对这个问题的目标纯粹是教育性的)
答案 0 :(得分:5)
使用awk
:
$ awk '/bar/ && FNR>2 {print li[-2]}
{li[-2]=li[-1]; li[-1]=$0}' file
hello my friend
foo
kouki
在匹配之前打印 n th 行可以更通用(不必将整个文件放在内存中):
$ awk -v n=3 '/bar/ && FNR>n{ print li[n]}
{for (i=n;i>1;i--)
li[i]=li[i-1]
li[1]=$0}' file
hello my friend
poof
答案 1 :(得分:4)
短 sed 方法:
sed -n '1N;2N;/bar[^\n]*$/P;N;D' file.txt
输出:
hello my friend
foo
kouki
详细:
1N;2N;
- 将前3行读入模式空间
/bar[^\n]*$/
- 检查最后一行是否与bar
匹配。 ([^\n]*$
- 确保它是捕获的3行部分的最后一行)
P;
- 如果找到上述匹配项,则打印模式空间的第1行
N
- 在模式空间中添加换行符,然后将下一行输入追加到模式空间
D
- 删除模式空间中直到第一个换行符的文本,然后使用生成的模式空间重新启动循环(即关于前3行 - 第1行hello my friend
将是从模式空间打印和删除,新周期将在下一行开始foo
)
答案 2 :(得分:2)
o=0 a=()
while read -r line;do
a+=("${line}")
[ "$line" ] && [ -z "${line//*bar*}" ] && echo ${a[o-2]}
((o++))
done <file.txt
hello my friend
foo
kouki
或者,因为您正在谈论 regex
:
while read -r line;do
a+=("${line}")
[[ ${line} =~ bar ]] && echo ${a[o-2]}
((o++))
done <file.txt
但是,对于表演,我更喜欢第一种语法......
grepIndex () {
local o=0 a=() line
while read -r line; do
a+=("${line}")
[ "$line" ] && [ -z "${line//*$1*}" ] && echo ${a[o-$2]}
((o++))
done
}
grepIndex <file.txt bar 2
hello my friend
foo
kouki
可以写作
grepIndex() {
local o=0 a=() line
while read -r line;do
a+=("${line}")
[[ ${line} =~ $1 ]] && echo ${a[o-$2]}
((o++))
done
}
太
如果 pure bash 在小文件上快得多,对于大文件,bash会变成 overkill !!看看RomanPerekhrest's answer!使用sed可以 这是最有效的解决方案之一(在大文件上)!