http://www.perlmonks.org?node_id=198884

Hello *

A few minutes ago a colleague of mine asked me if *NIX had a command that could be easily used to delete the 32nd line from a bunch of file. awk, sed and cat (and some combinations of them) came to mind, but I preferred a full Perl approach:

perl -i.bak -ne 'print unless ++$i == 32' filename

But since TIMTOWTDI, how would you do the same with a one liner (and, if you want to use modules, using only modules in bundle with perl)?

Ciao!
--bronto

# Another Perl edition of a song:
# The End, by The Beatles
END {
  $you->take($love) eq $you->made($love) ;
}

Replies are listed 'Best First'.
Re: Save all but line 32!
by davorg (Chancellor) on Sep 18, 2002 at 16:11 UTC

    Why introduce your own variable?

    perl -i.bak -ne 'print unless $. == 32' filename
    --
    <http://www.dave.org.uk>

    "The first rule of Perl club is you do not talk about Perl club."
    -- Chip Salzenberg

Re: Save all but line 32!
by jmcnamara (Monsignor) on Sep 18, 2002 at 17:00 UTC

    Here is another way:     perl -i -pe '$_=$}if$.==32' file

    Update: To handle multiple files it needs to be a little longer:     perl -i -pe '$_=$}if$.==32;$.=$[if+eof' file1 file2 ...

    But this is probably the cleanest way:     awk 'FNR != 32' file1 file2 ...

    --
    John.

Re: Save all but line 32!
by perrin (Chancellor) on Sep 18, 2002 at 16:30 UTC
    Not as fast and doesn't handle a list of files, but for entertainment value:
    perl -MTie::File -e 'tie @file, 'Tie::File', $ARGV[0]; splice @file, 3 +1, 1;' filename
    (Tie::File is in the standard lib now.)
      Some None of the other solutions will not correctly handle a list of files either (only the ones using $. will). And this one is easily extended to do so: perl -MTie::File -e 'tie(@file, 'Tie::File', $_) and splice @file, 31, 1 for @ARGV' file1 file2 file3 Update: zigdon is right. I should have tested.

      Makeshifts last the longest.

        I thought $. only resets on a close call? meaning the <> operator doesn't reset it, since it never closes the filehandles. So the following code will omit the 35th line only for the first file, but not for any other file:
        perl -ne 'print unless $. == 35' file1 file2
        Is that correct?

        -- Dan

Re: Save all but line 32!
by zigdon (Deacon) on Sep 18, 2002 at 16:14 UTC
    not very different, but should be slightly faster:
    perl -i.bak -ne 'if ($. < 32) { print } elsif ($. > 32) { print <>}' f +ilename
    I think it'll be faster since once you pass the 32nd line, you don't need to check anymore, you can just dump out the rest of the file. Note that $. might not do what you expect, depending on the value of $/. See perlvar.

    -- Dan

      Penny wise but pound foolish.

      Your program will read in all lines after line 32 before printing them. Usually that will be a lot slower than the comparison you save.

      I do not understand your remark about $/. You aren't setting it, nor does any of the switches influence it. How could $/ be any different from the default?

      My suggestion for the program:

      perl -i -nwe 'print unless 32 .. 32' filename
      Abigail
        This confuses me. I don't see how this would work. It looks like it will print no lines since 32 .. 32 should return either 1 (the number of elements in the list) or 32 (the last element in the list) both of which are "true". I am betting the later, but I am not sure. Would you kindly point out where I am wrong?
        perl -- executes perl
        -i   -- in-place editing
        -n   -- implict loop
        -w   -- turn on warnings (why?)
        -e   -- execute the following string as the program
        
        print    -- print what is in $_ (only executed if the unless is  false)
        unless   -- execute the previous command if the result of the following
                    expression is 0, undef, or '' (or the equivalent)
        32 .. 32 -- construct a list containing the numbers 32 through 32
        filename -- the file to edit
        
Re: Save all but line 32!
by cephas (Pilgrim) on Sep 18, 2002 at 17:18 UTC

      Very nice. ++

      I like the twisted logic but the best part is the bare $ at the end. I had to run it through B::Deparse to see what was happening:

      $ perl -MO=Deparse -pe '$_=$' LINE: while (defined($_ = <ARGV>)) { $_ = $; } continue { die "-p destination: $!\n" unless print $_; }

      It shows that in this case $ gets interpreted as $; the subscript separator. However, this means that your code inserts an extra character in the file as shown by this:     perl -pe '$.-32or$_=$' file | cat -A

      But I'm still not sure why perl inserts that semicolon. It doesn't in other cases:

      $ perl -MO=Deparse -pe '$_=$.' LINE: while (defined($_ = <ARGV>)) { $_ = $. } continue { die "-p destination: $!\n" unless print $_; }

      Anyone have an explanation for this last point?

      Update: blakem's answer below is right. This last case is a perl 5.005 issue with B::Deparse.

      --
      John.

        I seem to get the semicolon with your second case:
        % perl5.6.1 -MO=Deparse -pe '$_=$.' LINE: while (defined($_ = <ARGV>)) { $_ = $.; } continue { die "-p destination: $!\n" unless print $_; } % perl5.8.0 -MO=Deparse -pe '$_=$.' LINE: while (defined($_ = <ARGV>)) { $_ = $.; } continue { die "-p destination: $!\n" unless print $_; }
        Update: Response to above update: with 5.00503 I get:
        % perl5.00503 -MO=Deparse -pe '$_=$.' LINE: while (defined($_ = <ARGV>)) { $_ = $. } continue { die "-p destination: $!\n" unless print $_; }

        -Blake

      won't "32or" get interpreted as a single token?
        won't "32or" get interpreted as a single token?

        No it won't.

        or32 would be seen as an identifier, though.

(OT) Re: Save all but line 32!
by JayBonci (Curate) on Sep 19, 2002 at 07:59 UTC
    Offtopic for the site, but sed's a much better tool for that job. It's very easy to do with sed addressing:
    sed -n 32\!p filename
    Sadly, I don't have a clever or shorter way to do it in perl. The number 32 can be anything, of course

        --jb
      Golf time! :^) sed -e 32d filename

      Makeshifts last the longest.

        8 instead of 11 chars (excluding the filename) :^)

        t filename^G32^M^D^S

        T is my editor

        ^G32 := goto line 32

        ^M := return to terminate/execute the goto

        ^D := delete the line

        ^S := Save and exit!


        Cor! Like yer ring! ... HALO dammit! ... 'Ave it yer way! Hal-lo, Mister la-de-da. ... Like yer ring!
Way OT....
by ellem (Hermit) on Sep 19, 2002 at 02:35 UTC
    You wote:
    # Another Perl edition of a song: # The End, by The Beatles END { $you->take($love) eq $you->made($love) ; }
    John and Paul wrote:
    # Another Perl edition of a song: # The End, by The Beatles END { $you->take($love) eq $you->make($love) ; }

    --
    ellem@optonline.net
    There's more than one way to do it, but only some of them actually work.