5

I was looking on how I could sort a file based on the length of each sentence and I came across this snippet from this answer

perl -ne 'push @a, $_ } { print sort { length $a <=> length $b } @a' input
                      ^ ^  

I tested it and it works but I don't have a clue how this works! As far as I can see the syntax is wrong. It has an open right bracket and a non closed right bracket which I have marked.
I am really having trouble figuring out how to run perl commands like this in bash
Could some one please explain this snippet?

Community
  • 1
  • 1
Jim
  • 18,826
  • 34
  • 135
  • 254

3 Answers3

10

}{ is so called butterfly or Eskimo greeting Discovered by Abigail, in 1997.

Basically it closes while loop imposed by -n switch, and what follows }{ is block executed after while loop.

This is how perl parses this line,

perl -MO=Deparse -ne 'push @a, $_ } { print sort { length $a <=> length $b } @a' input

LINE: while (defined($_ = <ARGV>)) {
    push @a, $_;
}{ # <==here it is

    print((sort {length $a <=> length $b;} @a));
}

A more common way would be using END{} block instead of }{ operator:

perl -ne '
  push @a, $_ 
  END { 
    print sort { length $a <=> length $b } @a
  }
' input
mpapec
  • 50,217
  • 8
  • 67
  • 127
  • 1
    That's a nice link, although they have a mistake in there. The eskimo kiss does not work with the `-p` switch, only `-n`. – TLP Sep 15 '13 at 17:29
  • @TLP yes, `-p` has `continue` block so it doesnt work properly – mpapec Sep 15 '13 at 17:33
5

SzG's answer is correct: the script works because the -n switch causes Perl to literally wrap the code in the following loop:

LINE: while (<>) { # code goes here
;}

before compiling it. Thus, your example code becomes:

LINE: while (<>) { push @a, $_ } { print sort { length $a <=> length $b } @a
;}

which is a perfectly valid Perl script.


Anyway, a less hacky way to write that script would be:

perl -ne 'push @a, $_; END { print sort { length $a <=> length $b } @a }' input

or even just:

perl -e 'print sort { length $a <=> length $b } <>' input
Ilmari Karonen
  • 49,047
  • 9
  • 93
  • 153
  • Shouldn't the array `@a` be declared? – Jim Sep 15 '13 at 19:40
  • 1
    @Jim: We're not in [strict](http://perldoc.perl.org/strict.html) mode, so no, it's not necessary. Of course, for anything longer than a one-liner, you do want to `use strict` and declare your variables properly. – Ilmari Karonen Sep 15 '13 at 22:00
  • @IlmariKaronen:Still.The { push @a, $_ } is in the while scope so we read and push until EOF.But why does the print ` print sort { length $a <=> length $b } @a` is a loop?I run this line in a separate script like this: my @a=(5,6,2,3,7); print sort { length $a <=> length $b } @a; and it did not print the array sorted – Jim Sep 16 '13 at 06:30
  • @Jim: Please don't post your comments twice. Also, you should really ask a new question about that (if you can't figure out the answer yourself; hint: what's the sort block sorting by?) instead of posting it as a comment. – Ilmari Karonen Sep 16 '13 at 11:12
3

You run a Perl script specified on the command line with the -e script option. If you also add the -n option, Perl will surround the script with a

while(<>) {
    your-script-here
}

loop that reads standard input of the specified files line by line.

SzG
  • 12,333
  • 4
  • 28
  • 41
  • 1
    @Jim: It works because Perl _literally_ wraps your code in a `while (<>) { ... }` loop when you use the `-n` switch. So the actual code executed becomes `while (<>) { push @a, $_ } { print sort { length $a <=> length $b } @a }`. – Ilmari Karonen Sep 15 '13 at 17:17
  • 1
    `@a` is not declared anywhere.Shouldn't it be? – Jim Sep 15 '13 at 19:39
  • You don't need to declare stuff in Perl. They just spring to life when you use them. – SzG Sep 15 '13 at 20:33