Search code examples
perlone-liner

Conditional editing of line by one-liner


My question is more of an optimization one, rather then a "howto".
I have a lef file, with thousands of lines in the form of:

RECT 429.336 273.821 426.246 274.721 ;

I wanted to move left by 4 um all rects above a certain point using a one-liner:

perl -lane '$F[2] > 1200 ? print $F[0]," ", ($F[1] - 4)," ", $F[2]," ", ($F[3] -4)," ", $F[4], " ;" : print $_' trial.lef

Thing is, this is UGLY.
Is there a nicer way of editing the file?

I'm not picky and will be happy to have answers with other languages (awk, sed, etc.) as long as they are nicer than what I wrote.

Additional input:

      LAYER M12 ;
        RECT 0 411.214 1 412.214 ; <-- shouldn't change, because 411.214 < 1200
    END
  END kuku_pin
  PIN gaga_pin
    DIRECTION OUTPUT ;
    USE SIGNAL ;
    PORT
      LAYER M11 ;
        RECT 43.1045 1203.138 43.1805 1207.29 ; <-- should change to "RECT 39.1045 1203.138 39.1805 1207.29"
    END

Solution

  • There really is not much room for improvement, but you can replace -n with -p to skip the extra print. Further, you can edit the array elements and use join for a bit prettier code:

    perl -lape'if ($F[2] > 1200) { $F[1] -= 4; $F[3] -= 4; $_ = join " ", @F }'
    
    • -a autosplit mode, splits the line $_ on space and puts the values in the predefined @F array. This switch is used with -n or -p.
    • -p loops around the <> operator input, file or standard input
    • -= decreases the LHS by amount in RHS
    • join joins the line back together after math has been done
    • -l can be skipped in this case, since we never touch the line endings, but keeping it makes the code more flexible if we decide to edit the last field.

    When the condition is not met, original line is printed unchanged. Otherwise, it is replaced with the joined values in @F.

    If you decide to keep the leading whitespace before RECT you can surround your if-statement with

    if (($pre) = /^(\s*RECT)/)
    

    To store the beginning of the line, making the one-liner:

    perl -lape'if (($pre) = /^(\s*RECT)/) { if ($F[2] > 1200) { $F[1] -= 4; $F[3] -= 4; $F[0] = $pre; $_ = join " ", @F }}'