6

哇,这在标题中听起来很复杂,但我认为情况并非如此。

我有基本上具有这种布局的文本文件:

Stimulus ...
...
...
...
Response
Stimulus ...
...
...
...
Response

我使用 sed 获取介于两者之间的所有内容,然后进一步提取我需要的信息。

sed -n -e '/Stimulus/,/Response/ p'

但是,有时参与者没有响应,在这种情况下,文件如下所示:

Stimulus ...
...
...
...
Stimulus ...
...
...
...
Response

在这种特殊情况下,我的脚本不会得到我想要的东西。因此,我正在寻找一种方法来提取信息,当且仅当 pattern1 后面跟着 pattern2,而不是 pattern1 之后。

如果我表述不清楚,请告诉我。我更乐意提供更多信息。

4

6 回答 6

7

一种肮脏的方法,虽然它似乎在我的测试中有效,但可能是反转文件内容,从Responseto搜索Stimulus并再次反转结果。

假设以下输入数据:

Stimulus 1...
...
...
...
Stimulus 2...
...
...
...
Response 2
Stimulus 3...
...
...
...
Response 3
Stimulus 4...
...
...
...
Stimulus 5...

命令:

tac infile | sed -ne '/Response/,/Stimulus/ p' | tac -

产量:

Stimulus 2...
...
...
...
Response 2
Stimulus 3...
...
...
...
Response 3

编辑:例如带有独立Response部件的示例。有过滤两次(基于OP的评论):

tac infile | 
  sed -ne '/Response/,/Stimulus/ p' | 
  tac - | 
  sed -ne '/Stimulus/,/Response/ p'
于 2013-06-28T13:58:21.737 回答
5

这是一个纯解决方案:

tmp=()
while read l; do
  [[ $l =~ ^Stimulus ]] && tmp=("$l") && continue
  [ ${#tmp[@]} -eq 0 ] && continue
  tmp+=("$l")
  [[ $l =~ ^Response ]] && printf "%s\n" "${tmp[@]}" && tmp=()
done <infile

tmp如果找到以开头的列表,它将开始填充数组Stimulus。如果另一个Stimulus到达,它只是清除tmp并重新开始工作。如果Response找到,它会打印tmp数组的内容。实际上printf内置做了一个隐式循环。

输入:

cat >infile <<XXX
...
Response 0
...
Stimulus 1
...
Stimulus 2
...
Response 2
...
Stimulus 3
...
Response 3
...
Response 4
XXX

输出:

Stimulus 2
...
Response 2
Stimulus 3
...
Response 3
于 2013-06-28T14:08:44.317 回答
4

其他选项是 switch toperl及其触发器(范围运算符):

perl -lne '
    BEGIN {
        ## Create regular expression to match the initial and final words.
        ($from_re, $to_re) = map { qr/\A$_/ } qw|Stimulus Response|;
    }
    ## Range, similar to "sed".
    if ( $r = ( m/$from_re/o ... m/$to_re/o ) ) {
        ## If inside the range and found the initial word again, remove 
        ## all lines saved.
        if ( $r > 1 && m/$from_re/o ) {
            @data = ();
        }
        ## Save line.
        push @data, $_;
        ## At the end of the range, print all lines saved.
        if ( $r =~ m/E0\z/ ) {
            printf qq|%s\n|, join qq|\n|, @data;
            @data = ();
        }
    }
' infile

假设输入文件为:

Stimulus 1...
...
...
...
Stimulus 2...
...
...
...
Response 2
Stimulus 3...
...
...
...
Response 3
Stimulus 4...
...
...
...
Stimulus 5...

它产生:

Stimulus 2...
...
...
...
Response 2
Stimulus 3...
...
...
...
Response 3
于 2013-06-28T14:06:53.610 回答
4

这是一个纯粹的解决方案,它试图最大限度地减少愚蠢的副作用:

#!/bin/bash

out=()

while read -r l; do
   case "$l" in
       Stimulus*) out=( "$l" ) ;;
       Response*) ((${#out[@]}!=0)) && { printf "%s\n" "${out[@]}" "$l"; out=(); } ;;
       *) ((${#out[@]}!=0)) && out+=( "$l" ) ;;
   esac
done < infile

它还处理有 aResponse但没有的情况Stimulus

于 2013-06-28T14:19:29.683 回答
4

更新以处理孤立的响应

awk '
/Response/ { 
    if (p==1) {
        for(;k<length(a);) {
            print a[++k]
        }
        print $0
    }
    delete a;k=p=0
} 
/Stimulus/ {
    if (p==1) {
        delete a; i=0
    }
    p=1
} 
p { a[++i]=$0 }' log
于 2013-06-28T14:47:01.703 回答
4

来说,这是一项非常轻松且简单的工作,单向,没有不需要的管道和工具:

sed -n 'H;/^Stimulus/{h;d};/^Response/{x;s/^Response//;tk;p;:k;d}' file

输入文件:

刺激1...
坏的
坏的
坏的
刺激2...
...
...
...
回应 2
刺激 3...
...
...
...
回应 3
刺激 4...
坏的
坏的
坏的
坏的
刺激 5...
...
...
...
...
回应 5
坏的
坏的
坏的
坏的
回应 6
坏的
坏的
坏的

并输出:

$sed -n 'H;/^Stimulus/{h;d};/^Response/{x;s/^Response//;tk;p;:k;d}' 文件
刺激2...
...
...
...
回应 2
刺激 3...
...
...
...
回应 3
刺激 5...
...
...
...
...
回应 5

还有我的 GNU 代码:

awk '{a[++i]=$0};/^Response/ && a[1] !~ /^Response/ {for (k=1; k<=i; k++) {print a[k]}};/^Stimulus|^Response/ { delete a; i=0; a[++i]=$0}' file

如您所见,我需要太多 awk 代码...

于 2013-06-28T19:45:40.667 回答