awk range and selection of text

前端 未结 2 886
[愿得一人]
[愿得一人] 2021-01-23 09:49

I know how to use the range operator in awk

awk \'/start/,/stop/\' file

Is it possible to select text inside the range operator? ATM, I am usin

相关标签:
2条回答
  • 2021-01-23 09:53

    Never use a range expression as it makes trivial jobs very slightly briefer but then requires a complete rewrite or duplicate conditions when the task gets slightly more interesting.

    Instead of:

    awk '/start/,/stop/' file
    

    Use:

    awk '/start/{f=1} f{print} /stop/{f=0}' file
    

    and then what you want to do becomes simply:

    awk '/start/{f=1} f{ if ($1~/foo/) { } } /stop/{f=0}' file
    

    I'm assuming you have something in mind for inside the empty { }.

    0 讨论(0)
  • 2021-01-23 10:10

    Well what you have proposed does work:

    $ seq 1 15 | awk '/^6/,/^9/ { if ($1~/8/){print} else print "in range but not 8" }'
    in range but not 8
    in range but not 8
    8
    in range but not 8
    

    But as Ed Morton states, it is a brittle construct.

    Example, the , is the lowest precedence and you may scratch your head over why this does not work:

    $ echo "this one print" | awk '/^1/ , /^55/ || /this/'  
    

    Another example. Suppose you have:

    $ echo "a
    b
    c
    ---
    d
    e
    f
    ---
    g
    h"
    

    Try and use a range operator to include or exclude everything between ---. It is tricky since the start and end marks are the same and it is possible to set and reset a range on the same line.

    When if you train you muscle memory to not do /^x/ , /^y/ and instead do /^x/{flag=1} flag{whatever} /^y/{flag=0} then no more head scratching:

    $ echo "this one print" | awk '/^1/{flag=1} flag || /this/{print} /^55/{flag=0}'
    this one print
    

    Or,

    $ echo "a
    b
    c
    ---
    d
    e
    f
    ---
    g
    h" | awk '/^---$/{f= ! f; next}  f'
    d
    e
    f
    

    (Just change to ! f at the end if you want to reverse from include to exclude...)

    So in general, you can do this for a range and subrange:

    awk '/^start/ || /^end/{f= ! f; next} /e/ && f { what you do in sub range }' file
    
             ^         ^                            can be a single regex if same pattern
                                     ^              remove next to include in processing
                                          ^         applies to within range /start/,/end/
                                              ^  ^     because of the flag
    
    0 讨论(0)
提交回复
热议问题