sed recipe: how to do stuff between two patterns that can be either on one line or on two lines?

前端 未结 1 1662
粉色の甜心
粉色の甜心 2021-01-21 12:55

Let\'s say we want to do some substitutions only between some patterns, let them be and for clarity... (all right, all right,

相关标签:
1条回答
  • 2021-01-21 13:23

    This might work for you:

    # create multiline test data
    cat <<\! >/tmp/a
    > this
    > this { this needs
    > changing to
    > that } that
    > that
    > !
    sed '/{/!b;:a;/}/!{$q;N;ba};h;s/[^{]*{//;s/}.*//;s/this\|that/\U&/g;x;G;s/{[^}]*}\([^\n]*\)\n\(.*\)/{\2}\1/' /tmp/a
    this
    this { THIS needs
    changing to
    THAT } that
    that
    # convert multiline test data to a single line
    tr '\n' ' ' </tmp/a >/tmp/b
    sed '/{/!b;:a;/}/!{$q;N;ba};h;s/[^{]*{//;s/}.*//;s/this\|that/\U&/g;x;G;s/{[^}]*}\([^\n]*\)\n\(.*\)/{\2}\1/' /tmp/b
    this this { THIS needs changing to THAT } that that
    

    Explanation:

    • Read the data into the pattern space (PS). /{/!b;:a;/}/!{$q;N;ba}
    • Copy the data into the hold space (HS). h
    • Strip non-data from front and back of string. s/[^{]*{//;s/}.*//
    • Convert data e.g. s/this\|that/\U&/g
    • Swap to HS and append converted data. x;G
    • Replace old data with converted data.s/{[^}]*}\([^\n]*\)\n\(.*\)/{\2}\1/

    EDIT:

    A more complicated answer which I think caters for more than one block per line.

    # slurp file into pattern space (PS)
    :a
    $! {
    N
    ba
    }
    # check for presence of \v if so quit with exit value 1
    /\v/q1
    # replace original newlines with \v's
    y/\n/\v/
    # append a newline to PS as a delimiter
    G
    # copy PS to hold space (HS)
    h
    # starting from right to left delete everything but blocks
    :b
    s/\(.*\)\({.*}\).*\n/\1\n\2/
    tb
    # delete any non-block details form the start of the file
    s/.*\n//
    # PS contains only block details
    # do any block processing here e.g. uppercase this and that
    s/th\(is\|at\)/\U&/g
    # append ps to hs
    H
    # swap to HS
    x
    # replace each original block with its processed one from right to left
    :c
    s/\(.*\){.*}\(.*\)\n\n\(.*\)\({.*}\)/\1\n\n\4\2\3/
    tc
    # delete newlines
    s/\n//g
    # restore original newlines
    y/\v/\n/
    # done!
    

    N.B. This uses GNU specific options but could be tweaked to work with generic sed's.

    0 讨论(0)
提交回复
热议问题