从文件中读取索引“n”低于与给定正则表达式匹配的行的所有行

时间:2017-06-11 18:51:52

标签: bash file awk sed

我想从文件file.txt中读取所有索引n低于与给定正则表达式regex匹配的行的行。例如文件

hello my friend
foo
_bar_
I love this bar
poof
kouki
splash in the water
bar

如果regex=barn=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

是否有更好(更快,更易读)的解决方案?

(我对这个问题的目标纯粹是教育性的)

3 个答案:

答案 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可以 这是最有效的解决方案之一(在大文件上)!