I had to modify a file to remove duplicate lines and be sorted by length of line, so I used the following one-liner:
perl -plwe'$h{$_}++ } for (sort { length($a)<=>length($b) } keys %h) { +' infile >infile.tmp
and then moved the infile.tmp to infile. My task was accomplished, but I couldn't help but wonder if it could have been easily accomplished with inplace editing. I hadn't tried at first, because I thought the print would no longer go to the correct output file once the input file was completely read; a quick try confirmed this: when the implicit readline is returning undef, it also closes ARGVOUT and re-selects STDOUT. After a few seconds, the solution became obvious:
perl -i.bak -plwe'$h{$_}++; last if eof } for (sort { length($a)<=>len +gth($b) } keys %h) {' infile

Replies are listed 'Best First'.
•Re: Using -i in oneliner with postprocessing
by merlyn (Sage) on Apr 28, 2004 at 14:39 UTC
    The trick is that you need to be on the last line when you're printing the output. Here's a simple reverser to show how it works:
    perl -ni.bak -e 'push @a, $_; if (eof) { print reverse @a; @a = () }' +file1 file2 file3
    This reverses each of the contents of the files in place. Note that eof becomes true on the last line of each file, not just the last line of all files.

    -- Randal L. Schwartz, Perl hacker
    Be sure to read my standard disclaimer if this is a reply.

Re: Using -i in oneliner with postprocessing
by tkil (Monk) on Apr 28, 2004 at 08:29 UTC

    On the other paw, there's the shell version of a Schwartzian Transform:

    for i in *.txt do mv $i $i~ perl -ne 'print length, "\t", $_' $i~ | \ sort +0n | uniq | cut -f2- > $i done
Re: Using -i in oneliner with postprocessing
by tkil (Monk) on Apr 28, 2004 at 07:50 UTC

    When I think of postprocessing, I usually think of END blocks. Does this work for you?

    perl -i.bak -nwe '$h{$_} = length; END { print sort { $h{$a} <=> $h{$b} } keys %h; }' \ file1 file2 ...

    Update

    Oh, duh, just read your query a bit closer. Yeah, that's a stinker that it closes the file on you. And your solution is nifty (although the weirdly-matched braces scare me a bit).

      (although the weirdly-matched braces scare me a bit)
      Adding in -MO=Deparse:
      perl -MO=Deparse -plwe "$h{$_}++ } for (sort {length($a) <=> length($b +) } keys %h) {" BEGIN { $^W = 1; } BEGIN { $/ = "\n"; $\ = "\n"; } LINE: while (defined($_ = <ARGV>)) { chomp $_; ++$h{$_}; } foreach $_ (sort {length $a <=> length $b;} keys %h) { (); } continue { print $_; }
      The -p option wraps the script in a template equivalent to the following:
      while (<>) { your code here } continue { print $_ }
      If your code happens to have a closing curly, you can do all kinds of interesting things. My favorite was Abigail-II's substitute for wc, to count lines:
      perl -wlpe '}{*_=*.' filename

      -QM
      --
      Quantum Mechanics: The dreams stuff is made of

        The -p option wraps the script in a template equivalent to the following:

        Yes, I knew that. I just never thought that "equivalent" extended to actual placement of braces and presumption of certain syntactic constructs to the extent used by this trick.

        Speaking of which, is this (ab)use of -p blessed by p5p and/or Larry?

Re: Using -i in oneliner with postprocessing
by Roy Johnson (Monsignor) on Apr 28, 2004 at 15:01 UTC
    The inside-out braces are really freaky. Also, your solution won't work for multiple input files, while this will:
    perl -i.bak -nlwe '$h{$_}++; if (eof) {print for sort { length($a) <=> + length($b) } keys %h; %h=();}' junk.txt junk2.txt
    Update: D'oh! I knew to do that! Thanks, Merlyn.

    The PerlMonk tr/// Advocate