Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Perl - while (<>) file handling [duplicate]

A simple program with while( <> ) handles files given as arguments (./program 1.file 2.file 3.file) and standard input of Unix systems.

I think it concatenates them together in one file and work is line by line. The problem is, how do I know that I'm working with the first file? And then with the second one.

For a simple example, I want to print the file's content in one line.

while( <> ){
    print "\n" if (it's the second file already);
    print $_;
}
like image 585
Mantas Marcinkus Avatar asked Feb 17 '13 14:02

Mantas Marcinkus


3 Answers

The diamond operator does not concatenate the files, it just opens and reads them consecutively. How you control this depends on how you need it controlled. A simple way to check when we have read the last line of a file is to use eof:

while (<>) {
    chomp;             # remove newline
    print;             # print the line
    print "\n" if eof; # at end of file, print a newline
}

You can also consider a counter to keep track of which file in order you are processing

$counter++ if eof;

Note that this count will increase by one at the last line of the file, so do not use it prematurely.

If you want to keep track of line number $. in the current file handle, you can close the ARGV file handle to reset this counter:

while (<>) {
    print "line $. : ", $_;
    close ARGV if eof;
}
like image 178
TLP Avatar answered Nov 19 '22 05:11

TLP


The <> is a special case of the readline operator. It usually takes a filehandle: <$fh>.

If the filehandle is left out, then the the magic ARGV filehandle is used.

If no command line arguments are given, then ARGV is STDIN. If command line arguments are given, then ARGV will be opened to each of those in turn. This is similar to

# Pseudocode
while ($ARGV = shift @ARGV) {
  open ARGV, $ARGV or do{
    warn "Can't open $ARGV: $!";
    next;
  };
  while (<ARGV>) {
    ...; # your code
  }
}

The $ARGV variable is real, and holds the filename of the file currently opened.

Please be aware that the two-arg form of open (which is probably used here behind the scenes), is quite unsafe. The filename rm -rf * | may not do what you want.

like image 12
amon Avatar answered Nov 19 '22 04:11

amon


The name of the current file for <> is contained in special $ARGV variable.

You can cross-match your list of files from @ARGV parameter array with current file name to get the file's position in the list. Assuming the only parameters you expect are filenames, you can simply do:

my %filename_positions = map { ( $ARGV[$_] => $_ ) } 0..$#ARGV;

while (<>) {
    my $file_number = $filename_positions{$ARGV};
    #... if ($file_number == 0) { #first file     
}
like image 2
DVK Avatar answered Nov 19 '22 04:11

DVK