grep / awk / perl / sed - 打印所有匹配X的行,忽略只有YX的行并包含具有YX ZX的行

时间:2014-11-12 21:05:13

标签: regex perl awk sed grep

我有一个文件(tmp.txt),如下所示:

 first
 first rst
 allrst

printf“first \ nfirst rst \ nallrst \ n”> tmp.txt;

我想要:

 first rst
 allrst

有没有办法搜索'rst',但是如果只在第一个中找到rst,则排除匹配?

我试过了:

awk '(/rst/ && /first/) || (/rst/ && !/first/)' tmp.txt

9 个答案:

答案 0 :(得分:1)

使用grep而不使用-P选项:

grep 'rst' file | grep -v '^first$'
first rst
allrst

使用grep -P

grep -P '^(?!first$).*rst' file
first rst
allrst

答案 1 :(得分:0)

^(?=.*(?<!fi)rst).*$

在Perl中试试。参见演示。

http://regex101.com/r/kP8uF5/13

答案 2 :(得分:0)

或vks回答的变体

^(.*(?<!fi)rst.*)$

Demo

^               Start of string
(               Capturing Group \1
  .             Any character except line break
  *             (zero or more)(greedy)
  (?<!          Negative Look-Behind
    fi          "fi"
  )             End of Negative Look-Behind
  rst           "rst"
  .             Any character except line break
  *             (zero or more)(greedy)
)               End of Capturing Group \1
$               End of string

答案 3 :(得分:0)

Perl中的正则表达式为/^.*(?<!fi)rst.*/m

 ^ .* 
 (?<! fi )
 rst .* 

答案 4 :(得分:0)

您没有定义,rst必须位于该行的末尾,因此这可能适用于您的要求:

grep -P '(?<!fi)rst' file

答案 5 :(得分:0)

这可能适合你(GNU sed):

sed 'h;s/first//g;/rst/!d;g' file

制作当前行的副本。删除所有first并检查rst的剩余行。如果字符串在更改的行中,则打印副本,否则删除该行。

答案 6 :(得分:0)

这符合您的所有要求:

perl -ne 'print if /^(?=.*first)(?=.*(?<!fi)rst)/; next if /first/; print if /rst/'
  1. line has&#34; first&#34;和非第一个&#34; rst&#34;:打印
  2. 行已经&#34;首先&#34;:跳过它
  3. 行已经&#34; rst&#34;:打印
  4. 或者,正如你的标题所示,象征性地

    perl -ne '
      BEGIN {$x="rst"; $y="fi"; $z="(?<!fi)"}
      print if /^(?=.*$y$x)(?=.*$z$x)/; next if /$y$x/; print if /$x/
    '
    

答案 7 :(得分:0)

恕我直言,没有精确指定模式。如果该行包含afirstrstfirst,该怎么办?所以我创建了两个版本。我假设每行包含字符串rst。哪个不是,它不需要打印。我更喜欢针对的解决方案,因为后者使用了更多资源来启动,并且此任务并不真正需要。

第一个版本会检查包含rst但不等于first的所有字词。如果找到一个,则打印该行。

awk '/rst/ {
  for(i=1;i<=NF&&$i~/rst/&&$i=="first";++i);
}i<=NF' inputfile

输入文件:

first
first rst
allrst
afirst
rstfirst

输出继电器:

first rst
allrst
afirst
rstfirst

另一个解决方案检查所有rst,然后添加前两个字符(如果适用)。如果结果字符串不是first,则它会打印该行。 (类似于sln的负面后置缓冲区解决方案)

awk '/rst/ {
  for(s=$0;i=index(s,"rst");s=substr(s,i+1)) 
    if (i<2 || substr(s,i-2,5)!="first") {print; break}
}' inputfile

输出:

first rst
allrst
rstfirst

我希望这有点帮助!

答案 8 :(得分:0)

sed -n '1!{/rst/p}' tmp.txt

如果不在第一行,则打印与行中第一个模式匹配的匹配项,或者如果您想先计算第一个但不是第一个 - 仅当第一个也在另一个字符串中时:

sed -n '/[^f][^i]rst/p' tmp.txt 

或者如果您想使用Bash shell,您可以更灵活地满足类似要求:

while read -r a; do  
 num_first=$(echo "$a" | grep -c 'first');
 num_rst=$(echo "$a" | sed 's/first//g' | grep -c 'rst'); 
 if [[ $num_rst+1 -gt $num_first ]]; then 
   echo "$a"; 
 fi done < tmp.txt

此代码首先使用grep进行计数,然后使用rst进行计数 - 只有当rst首先超过字符串时才会打印字符串。

作为一个单行:

while read -r a; do num_first=$(echo "$a" | grep -c 'first');num_rst=$(echo "$a" | sed 's/first//g' | grep -c 'rst'); if [[ $num_rst+1 -gt $num_first ]]; then echo "$a"; fi done < myfile