shell 计算文件中两个模式匹配之间的行数,只有当计数大于4时才删除模式匹配之间的所有行

liwlm1x9  于 2023-05-29  发布在  Shell
关注(0)|答案(4)|浏览(143)

我有一个包含如下文本的文件
输入文件:

------start---------
first line
second line
third line 
fourth line
------end-----------
xyx
pqr
------start---------
first line
second line
third line 
fourth line
fith line
sixth line
------end-----------

我想要一个输出文件,这样,如果模式之间的行“开始”和“结束”是超过4所有我想删除模式之间的所有这些行,否则我不希望接触他们,如果计数小于或等于4
我需要像下面这样的输出文件,其中我想删除两个模式匹配之间的所有行,只有当行的总数超过4
预期输出文件:

------start---------
first line
second line
third line 
fourth line
------end-----------
xyx
pqr
------start---------
------end-----------

我使用了这个sed命令

sed -i '/start/,/end/{//!d}' filename

但是它将不覆盖当模式匹配之间的总行数小于或等于4时避免删除所有行的情况

svujldwt

svujldwt1#

使用任何awk,而不是一次阅读所有输入到内存中:

awk '
    inBlock {
        if ( /^-+end-+$/ ) {
            if ( gsub(/\n/,"&",rec) <= 4 ) {
                printf "%s", rec
            }
            inBlock = rec = ""
        }
        else {
            rec = rec $0 ORS
        }
    }
    !inBlock {
        print
    }
    /^-+start-+$/ {
        inBlock = 1
    }
' file
------start---------
first line
second line
third line
fourth line
------end-----------
xyx
pqr
------start---------
------end-----------
laximzn5

laximzn52#

使用GNU sed

$ sed -Ezi.bak 's/(-+start-+\n)(([^-\n]*\n){5,})(-+end-+\n)/\1\4/g' input_file
------start---------
first line
second line
third line
fourth line
------end-----------
xyx
pqr
------start---------
------end-----------
eivnm1vs

eivnm1vs3#

你可以用类似的方式使用perl代替sed

perl -0pe 's/------start---------\n\K(?:(?:(?!------(?:end|start)---------).)+?\n){5,}(?=------end-----------)//g' myfile

这里perl -0pesed -z的替代品,但它支持PCRE正则表达式,包括lookaheads和捕获重置\K
Regex本身意味着:

  • ------start---------\n-以换行符开始标记,
  • \K忽略先前匹配的序列,停留在相同的光标位置。替代它的可能是更广为人知的(?<=------start---------\n),但\K具有更好的性能。
  • (?:(?:(?!------(?:end|start)---------).)+?\n){5,}-至少5行不包含开始或结束标签,
  • (?=------end-----------) lookahead检查匹配的序列后面是否有结束标记。

正则表达式here的演示。

bvjveswy

bvjveswy4#

awk \
    -v s1='------start---------' \
    -v s2='------end-----------' \
'
    # accumulate lines between markers
    between { data = data RS $0 }

    # found start marker
    $0==s1 {
        # reset state
        between = NR
        data = $0
    }

    # print anything outside markers
    !between;

    # found end marker
    $0==s2 {
        # print data if fewer than 6 lines total
        #     (including the two marker lines)
        # else just print the markers 
        print (NR < between+6) ? data : s1 RS s2

        # reset state
        between = 0
        data = ""
    }
' filename

相关问题