sed:通过模式A的最后匹配打印所有行,然后仅打印与模式B匹配的行

时间:2018-11-27 20:10:35

标签: shell perl awk sed scripting

尊敬的同事...

我生成几千行格式的日志文件:

a
b
X
d
X
e
b
g
Y
a
Y
d

例如,我希望脚本的输出通过“ ^ X”的最后一个匹配项打印所有行,然后仅打印“ ^ Y”的匹配项。以上所需的输出:

a
b
X
d
X
Y
Y

“ X”将始终出现,但“ Y”可能不会出现。如果未出现“ Y”,我相信最后一个“ X”将位于文件的最后一行。

使用“ sed”很难做到这一点,尽管那是我一直在尝试的方法。我使用“ tac”翻转行顺序,因此可以通过匹配“ ^ X”的 FIRST 来删除不匹配“ ^ Y”的任何内容。因为我不使用“ -n”,所以在第一个匹配“ ^ X”之后,将回显所有行。我只是再次使用“ tac”将其翻转并放入文件中。

似乎可以正常工作...

tac /path/to/logfile | \
sed -e '1,/^X/ { /^Y/!d }' | \
tac > /output/path/logfile.processed

不...?

PS:“ tac”是否在所有Linux上都普遍可用?

3 个答案:

答案 0 :(得分:4)

没有tac的情况下,使用awk的双程方法

$ awk 'NR==FNR{if(/^X$/) lx=NR; next} FNR<=lx || /^Y$/' file{,}

a
b
X
d
X
Y
Y

标记X的最后一个索引,并在该索引和其他匹配模式之前打印所有内容。

答案 1 :(得分:2)

为避免阅读两次,可以使用perl

$ perl -0777 -lnE 'say $1 while (/(\A[\s\S]*^X$|^Y$)/gm)' file
a
b
X
d
X
Y
Y

或者,使用sed和常用实用程序:

$ sed_cmd=$(printf "1,%sp; /^Y/p" $(cat -n file | sed -nE 's/^[[:space:]]*([[:digit:]][[:digit:]]*)[[:space:]]*X/\1/p' | tail -n 1))
$ sed -nE "$sed_cmd" file
# same output

答案 2 :(得分:0)

这是使用Perl的逻辑上更明确的版本。

perl -MList::Util=max -lnE '
    $lines{$.} = $_; 
    eof || next; 
    $last_match = max grep {$lines{$_} =~ /^X/} keys %lines;
    say for @lines{1 .. $last_match};
    say for grep {$_ =~ /^Y/} @lines{$last_match .. $.};
' /path/to/logfile