Search code examples
perlfilefilehandler

File handles not working properly in Perl


I tried initializing two file handles to NULL and later use it in my program.

This was my code:

my $fh1 = " ";
my $fh2 = " ";
open($fh1, ">", $fname1);
open($fh2, ">", $fname2);
print $fh1 "HI this is fh1";

After executing, my files contained this:

fname1 is empty

fname2 cointains

Hi this is fh1

What was the mistake ?

Why is fname1 empty while fname2 contains a string, even though I haven't inserted any string in fh2?


Solution

  • You have set $fh1 and $fh2 to the same value (a space character, not NULL) and so they refer to the same underlying typeglob for I/O.

    Filehandles in Perl are a special variable type called a glob or typeglob. In the old days of Perl 4, you always referred to a glob as a character string, often as a bareword. The barewords STDIN, STDOUT, and STDERR are relics of this simpler time.

    Nowadays, you can (and usually should) use lexical filehandles, but the underlying reference to a typeglob will still be there. For example, you can write

    my $fh = 'STDOUT';
    print $fh "hello world\n";
    

    and this will do the exact same thing as

    print STDOUT "hello world\n";
    

    Now if you pass an uninitialized scalar as the first argument to open, Perl will assign an arbitrary typeglob to it. You probably don't need to know which typeglob it is.

    But if the argument to open is already initialized, Perl uses the typeglob with that argument's value. So this snippet of code will create and add data to a file:

    my $fh = "FOO";
    open $fh, '>', '/tmp/1';
    print FOO "This is going into /tmp/1\n";
    close $fh;
    

    Now we can look at your example. You have set $fh1 and $fh2 to the same value -- a string consisting of a space character. So your open call to $fh1 creates an association between a typeglob named " " and the file descriptor for the output stream to $fname1.

    When you call open on $fh2, you are reusing the typeglob named " ", which will automatically close the other filehandle using the same typeglob ($fh1), the same was as if you say open FOO, ">/tmp/1"; open FOO, ">/tmp/2", the second open call will implicitly close the first filehandle.

    Now you are printing on $fh1, which refers to the typeglob named " ", which is associated with the output stream to file $fname2, and that's where the output goes.

    It was a mistake to initialize $fh1 and $fh2. Just leave them undefined:

    my ($fh1, $fh2);
    open $fh1, ">", ...   # assigns $fh1 to arbitrary typeglob
    open $fh2, ">", ...   # assigns $fh2 to different arbitrary typeglob