How do I match multiple addresses in sed?

2019-02-13 18:10发布

问题:

I want to execute some sed command for any line that matches either the and or or of multiple commands: e.g., sed '50,70/abc/d' would delete all lines in range 50,70 that match /abc/, or a way to do sed -e '10,20s/complicated/regex/' -e '30,40s/complicated/regex/ without having to retype s/compicated/regex/

回答1:

Logical-and

The and part can be done with braces:

sed '50,70{/abc/d;}'

Further, braces can be nested for multiple and conditions.

(The above was tested under GNU sed. BSD sed may differ in small but frustrating details.)

Logical-or

The or part can be handled with branching:

sed -e '10,20{b cr;}' -e '30,40{b cr;}' -e b -e :cr -e 's/complicated/regex/' file
  • 10,20{b cr;}

    For all lines from 10 through 20, we branch to label cr

  • 30,40{b cr;}

    For all lines from 30 through 40, we branch to label cr

  • b

    For all other lines, we skip the rest of the commands.

  • :cr

    This marks the label cr

  • s/complicated/regex/

    This performs the substitution on lines which branched to cr.

With GNU sed, the syntax for the above can be shortened a bit to:

sed '10,20{b cr}; 30,40{b cr}; b; :cr; s/complicated/regex/' file


回答2:

To delete lines from 10 to 20 and 30 to 40 matching your complicated regex with GNU sed:

sed -e '10,20bA;30,40bA;b;:A;s/complicated/regex/;d' file

or:

sed -e '10,20bA' -e '30,40bA' -e 'b;:A;s/complicated/regex/;d' file


bA: jump to label :A

b: a jump without label -> jump to end of script

d: delete line



回答3:

I don't think sed has the facility for multiple selection criteria, my advice would be to step up to awk, where you can do something like:

awk 'NR >= 50 && NR <= 70 && /abc/ {next} {print}' inputFile
awk '(NR >= 10 and NR <= 20) || (NR >= 30 && NR <= 40) {
     sub("from-regex", "to-string", $0); print }'


回答4:

sed is excellent for simple substitutions on individual lines but for anything else just use awk for clarity, robustness, portability, maintainability, etc...

awk '
(NR>=50 && NR<=70) && /abc/ { next }
(NR>=10 && NR<=20) || (NR>=30 && NR<=40) { sub(/complicated/,"regex") }
{ print }
' file