1

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
toolic
  • 57,801
  • 17
  • 75
  • 117
user2141046
  • 862
  • 2
  • 7
  • 21
  • 1
    "Ugly" is highly subjective. Would you prefer `perl -lane 'map $_ -= 4, @F[1,3] if $F[2] > 1200; print "@F"' trial.lef`? – jo-37 Jul 25 '21 at 16:58
  • Sure. I didn't know the map function and you just introduced it to me! That's nicer than what I wrote (as far as I see) – user2141046 Jul 30 '21 at 07:32
  • 1
    Actually, my code is abusing `map`. It exploits the feature that `$_` is an alias to the list value and can be (ab)used to modify it. So I'd say the code is short but obscure. It might be suitable for a one-liner, but never ever for production code. – jo-37 Jul 30 '21 at 19:06

1 Answers1

3

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 }}'
TLP
  • 66,756
  • 10
  • 92
  • 149
  • 1
    Hmmm, well - it is kind of cheating, you crammed 3 lines into a single parenthesis, but seems valid enough. not accepting it now, to see if other interesting solutions will come up... – user2141046 Jul 25 '21 at 15:26
  • 1
    It is common practice in one-liners to use multiple lines separated with semi-colon, there's no cheating there. One difference between my code and yours, however, is that your code will truncate your lines after the 5th field, and mine will not. – TLP Jul 25 '21 at 15:33
  • Also if you want to keep the whitespace in front of the line, some adjustment to your code might be needed. – TLP Jul 25 '21 at 15:34
  • yeah, whitespaces are non-issue. the truncating is 1-0 for you, I'll give you that. – user2141046 Jul 25 '21 at 15:38
  • 1
    Still, keeping the formatting is nice, so I found a way. – TLP Jul 25 '21 at 16:02
  • I already ranked your answer, can't rank it again, but the whitespaces fix is an upgrade! – user2141046 Jul 26 '21 at 05:40