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

muchyog has asked for the wisdom of the Perl Monks concerning the following question:

Hi I am trying to read line no 17 to 30 of a file and write the output to a new file. I am able to do this using a command line :
perl -ne 'print if $. >= 17 && $. <= 30' infile.txt > output.txt

But the same thing I try doing within script I am unable to Here is the code which I am using:

#/usr/bin/perl -w my $outFile= ">/home/eyogmuc/Yogesh/Technical/Perl/output.txt"; my $inFile= "/home/eyogmuc/Yogesh/Technical/Perl/infile.txt"; open INFILE, $inFile or die "Can't open $inFile : $!"; open OUTFILE, $outFile or die "Can't open $outFile : $!"; #system(`print if $. >= 17 && $. <= 30 INFILE >> OUTFILE`); system("'print if $. >= 17 && $. <= 30' infile.txt > output.txt" ); close INFILE or die "can't close $inFile : $!"; close OUTFILE or die "can't close $outFile : $!";

Let me know the right way of doing this.

Replies are listed 'Best First'.
Re: System command not working
by NetWallah (Canon) on Jul 10, 2012 at 06:03 UTC
    The "-n" option in your one-liner does the file reading for you.

    Your call to "system" is meaningless , so please take that out.

    You will need to READ the INfile using something like <INFILE>.

    You can track the record number with $. .

    Write to the outfile using "print OUTFILE ...".

    Please read the perl documentation on how to read and write files - these are documented along with the "open" function.

                 I hope life isn't a big joke, because I don't get it.
                       -SNL

Re: System command not working
by kcott (Archbishop) on Jul 10, 2012 at 06:43 UTC

    You can see what's happening on the commandline like this:

    $ perl -MO=Deparse,p -ne 'print if $. >= 17 && $. <= 30' LINE: while (defined($_ = <ARGV>)) { print $_ if $. >= 17 and $. <= 30; } -e syntax OK

    Now that the while loop has been revealed, you can use this code as the basis of your script.

    -- Ken

      Thanks for the reply I am doing it in this way :

      #/usr/bin/perl -w use strict ; my $outFile= "output.txt"; my $inFile= "infile.txt"; my $start=17; my $end=30; my $perlcmd="perl -ne 'print if \$. >= $start && \$. <= $end' $inFile +>>$outFile"; system($perlcmd);

      This is working what I expect but plz tell is this the best way or their is a fast and better way.

        From your initial shell process, you launch a perl process which launches another shell process which launches another perl process. No, this is not the best way to do things. Except for the system() function, you initially seemed to be on the right track. Here's how I would have tackled this:

        #!/usr/bin/env perl use strict; use warnings; my $infile = q{pm_read_part_file.in}; my $outfile = q{pm_read_part_file.out}; my $start = 3; my $end = 5; open my $in_fh, q{<}, $infile or die qq{Can't open $infile for reading +: $!}; open my $out_fh, q{>}, $outfile or die qq{Can't open $outfile for writ +ing: $!}; while (<$in_fh>) { next if $. < $start; last if $. > $end; print $out_fh $_; } close $in_fh; close $out_fh;

        Here's a sample run with file contents:

        en@ganymede: ~/tmp $ pm_read_part_file.pl ken@ganymede: ~/tmp $ cat pm_read_part_file.in Line 1 Line 2 Line 3 Line 4 Line 5 Line 6 ken@ganymede: ~/tmp $ cat pm_read_part_file.out Line 3 Line 4 Line 5 ken@ganymede: ~/tmp $

        -- Ken

        You can add -n to the first line of your script and set all needed variables in the BEGIN{} block (which is not looped like all other code when using -n):
        #!/usr/bin/perl -wn BEGIN{ use strict; my $outFile= "output.txt"; my $inFile= "infile.txt"; $main::start=17; # 'my' in this scope will result in uninitialized va +lues outside the BEGIN{} block $main::end=30; close STDOUT; close STDIN; open STDIN,"<",$inFile || die $!; open STDOUT,">",$outFile || die $!; } print if $. >= $start && $. <= $end;
        Sorry if my advice was wrong.

        Any time I invoke anything on the OS via 'system' or backticks or any other way, I'd be worried that the program being called may not be in the path of the user running the script.

        If you prefer running your one-liner as an OS command from inside a script, why not run it from a shell script then?

        #!/usr/bin/sh /usr/bin/perl -ne 'print if $. >=17 && $. <= 39' infile.txt >outfile.t +xt

        It seems someone taught you how to program outside of Perl before she taught you how to program inside of Perl. ;-)

        Here's my riff on kcott's script…

        #!/usr/bin/env perl use strict; use warnings; use autodie qw( open close ); use English qw( -no_match_vars ); my $input_file = 'input.txt'; my $output_file = 'output.txt'; my $start = 17; my $end = 30; open my $input_fh, '<', $input_file; open my $output_fh, '>', $output_file; LINE: while (my $line = <$input_fh>) { next LINE if $INPUT_LINE_NUMBER < $start; last LINE if $INPUT_LINE_NUMBER > $end; print {$output_fh} $line; } close $input_fh; close $output_fh; exit 0;

        And here's a greatly reduced version using <ARGV> and STDOUT instead of hardwiring file names inside the script…

        #!/usr/bin/env perl use warnings; while (<>) { next if $. < 17; last if $. > 30; print; }
Re: System command not working
by jwkrahn (Abbot) on Jul 10, 2012 at 09:50 UTC
    perl -ne 'print if $. >= 17 && $. <= 30' infile.txt > output.txt

    That can be written more simply as:

    perl -ne 'print if 17 .. 30' infile.txt > output.txt

    The comparison to $. is done automatically.

    Your program needs a while loop:

    open INFILE, '<', $inFile or die "Can't open $inFile : $!"; open OUTFILE, '>', $outFile or die "Can't open $outFile : $!"; while ( <INFILE> ) { print OUTFILE if 17 .. 30; } close INFILE or die "can't close $inFile : $!"; close OUTFILE or die "can't close $outFile : $!";
Re: System command not working
by monsoon (Pilgrim) on Jul 10, 2012 at 06:09 UTC
    'system' calls OS command. OS 'print' command is much different from perl 'print' function.