8

我有一个问题,我想解析文件的输出,我想在两个模式之间获取第 n 次出现的文本,最好使用 awk 或 sed

category
1
s
t
done
category
2
n
d
done
category
3
r
d
done
category
4
t
h
done

假设对于这个例子,我想在 category 和 done 之间获取第三次出现的文本,基本上输出是

category
3
r
d
done
4

5 回答 5

8

这可能对您有用(GNU sed):

'sed -n '/category/{:a;N;/done/!ba;x;s/^/x/;/^x\{3\}$/{x;p;q};x}' file

-n使用该选项关闭自动打印。收集 和 之间的category线done。在保持空间中存储一个计数器,当它达到 3 时,在模式空间中打印集合并退出。

或者,如果您更喜欢 awk:

awk  '/^category/,/^done/{if(++m==1)n++;if(n==3)print;if(/^done/)m=0}'  file
于 2012-11-08T07:10:03.843 回答
7

尝试这样做:

 awk -v n=3 '/^category/{l++} (l==n){print}' file.txt

或者更神秘:

awk -v n=3 '/^category/{l++} l==n' file.txt

如果您的文件很大:

awk -v n=3 '/^category/{l++} l>n{exit} l==n' file.txt
于 2012-11-08T02:30:51.947 回答
2

如果您的文件不包含任何空字符,则正在使用GNU sed. 这将找到模式范围的第三次出现。但是,您可以轻松地对其进行修改以获得您想要的任何事件。

sed -n '/^category/ { x; s/^/\x0/; /^\x0\{3\}$/ { x; :a; p; /done/q; n; ba }; x }' file.txt

结果:

category
3
r
d
done

解释:

使用开关关闭默认打印-n。在行首匹配单词“category”。将模式空间与保持空间交换,并将空字符附加到模式的开头。在示例中,如果模式随后包含两个前导空字符,则将模式拉出保持空间。现在创建一个循环并打印模式空间的内容,直到匹配最后一个模式。当找到最后一个模式时,sed将退出。如果没有找到sed,将继续读取下一行输入并继续循环。

于 2012-11-08T04:00:31.117 回答
1
awk -v tgt=3 '
/^category$/ { fnd=1; rec="" }

fnd {
   rec = rec $0 ORS
   if (/^done$/) {
      if (++cnt == tgt) {
         printf "%s",rec
         exit
      }
      fnd = 0
   }
}
' file
于 2012-11-08T13:20:58.900 回答
0

使用 GNU awk,您可以将记录分隔符设置为正则表达式:

<file awk 'NR==n+1 { print rt, $0 } { rt = RT }' RS='\\<category' ORS='' n=3

输出:

category 
3
r
d
done

RT是匹配的记录分隔符。请注意,相对于 的记录n将关闭一个,因为第一条记录指的是第一条之前的记录RS

编辑

根据 Ed 的评论,当记录之间有其他数据时,这将不起作用,例如:

category
1
s
t
done
category
2
n
d
done

foo

category
3
r
d
done

bar
category
4
t
h
done

解决此问题的一种方法是使用第二个(或第一个)awk 清理输入:

<file awk '/^category$/,/^done$/' |
  awk 'NR==n+1 { print rt, $0 } { rt = RT }' RS='\\<category' ORS='' n=3

输出:

category 
3
r
d
done

编辑 2

正如Ed在评论中指出的那样,上述方法不搜索结束模式。其他答案未涵盖的一种方法是使用(请注意,awk getlinegetline有一些警告):

<file awk '
  /^category$/ {
    v = $0
    while(!/^done$/) { 
      if(!getline) 
        exit
      v = v ORS $0
    }
    if(++nr == n) 
      print v
}' n=3

一行:

<file awk '/^category$/ { v = $0; while(!/^done$/) { if(!getline) exit; v = v ORS $0 } if(++nr == n)  print v }' n=3
于 2012-11-08T08:30:27.310 回答