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

Thanks to graff's response to this node, I have the following subs. My problem is that, after building a HoHoL (%anchors), I can't print to the filehandles contained in the outermost hash key.
Here's what I get:
Can't use string ("*main::INCUNIX") as a symbol ref while "strict refs +" in use at ./nbu-daily-3.pl line 502, <BPDBJOBS> line 2044.
I use filehandles contained in variables elsewhere in the code, only in the successful instance, I don't push them into a hash. It seems that that shouldn't matter; I'm still writing to a filehandle contained in a variable.

Any thoughts?

# for the sake of brevity, I'll include just the relavent code # here's how I open the files to begin with. my @incHandles = (*INCNT, *INCMAIL, *INCUNIX); my $filepath = "/opt/apache/htdocs/nbu/reports"; open $incHandles[0], ">$filepath/inc.nt" or die "cannot open $filep +ath/inc.nt for writing: $!\n"; open $incHandles[1], ">$filepath/inc.mail" or die "cannot open $filep +ath/inc.mail for writing: $!\n"; open $incHandles[2], ">$filepath/inc.unix" or die "cannot open $filep +ath/inc.unix for writing: $!\n"; # then, as I iterate through a couple of hashes, # amongst other things, I call the following two subs ################################################################## sub printTableRow { my ( $outHandle, $status, $class, $incFlag ) = @_; # Hash of colors my %colors = ( active => "#005154", failed => "#ff0000", queued => "#622188", partial => "#ffb90f", success => "#00ff00" ); # Hash of titles my %titles = ( active => "Active Jobs", failed => "Failed Jobs", queued => "Queued Jobs", partial => "Partially Successful Jobs", success => "Successful Jobs" ); $incString = "<a href=\"\#$class\.$titles{$status}\"><font color=\"$co +lors{$status}\">$class</a></font><br>"; my $incHandle; if ( $incFlag eq "N" ) { $incHandle = *INCnt; } if ( $incFlag eq "M" ) { $incHandle = *INCmail; } if ( $incFlag eq "U" ) { $incHandle = *INCunix; } push ( @{$anchors{$incHandle}{$status}}, $incString ); #if ( $incFlag eq "N" ) { print INCnt "$incString\n"; } #if ( $incFlag eq "M" ) { print INCmail "$incString\n"; } #if ( $incFlag eq "U" ) { print INCunix "$incString\n"; } print $outHandle <<ROW; \n<table border=1 width="100%">\n <tr> <td colspan="12" align="left" valign="center" bgcolor="$colors{$sta +tus}" > <b><font size="+2"><a name=\"$class\.$titles{$status}\"</a>$clas +s: $titles{$status}</b></font> </td> </tr> ROW } ################################################################## # print include files for SSI sub printIncludes { my ( $incHandle, $status ); for $incHandle ( keys %anchors ) { for $status ( keys %{ $anchors{$incHandle}} ) { print $incHandle "@{ $anchors{$incHandle}{$status} }\n"; } } } ##################################################################
An example of %anchors:
$VAR3 = '*main::INCUNIX'; $VAR4 = { 'failed' => [ '<a href="#MII-AFS-Filesystem.Failed Jobs"><fo +nt color="#ff0000">MII-AFS-Filesystem</a></font><br>', '<a href="#MII-Media.Failed Jobs"><font color= +"#ff0000">MII-Media</a></font><br>', '<a href="#UNIX-ESMEC.Failed Jobs"><font color +="#ff0000">UNIX-ESMEC</a></font><br>' ], 'success' => [ '<a href="#DB_BACKUP.Successful Jobs"><font c +olor="#00ff00">DB_BACKUP</a></font><br>', '<a href="#HRMS-NT.Successful Jobs"><font col +or="#00ff00">HRMS-NT</a></font><br>', '<a href="#MII-AFS-Filesys-DR.Successful Jobs +"><font color="#00ff00">MII-AFS-Filesys-DR</a></font><br>', '<a href="#MII-Info.Successful Jobs"><font co +lor="#00ff00">MII-Info</a></font><br>', '<a href="#MII-Tuesday.Successful Jobs"><font + color="#00ff00">MII-Tuesday</a></font><br>' ] };

Replies are listed 'Best First'.
•Re: Problems writing to filehandles contained in variables
by merlyn (Sage) on Aug 10, 2002 at 18:01 UTC
    You can't store a glob into a variable and then use that variable. You can store a reference to a glob into a variable. If you replace
    my @incHandles = (*INCNT, *INCMAIL, *INCUNIX);
    with
    my @incHandles = (\*INCNT, \*INCMAIL, \*INCUNIX);
    it'll start to work. But the proper answer is to use IO::File objects, to keep you from even having to mention names for things that are never again referred as names:
    my @incHandles; use IO::File; push @incHandles, IO::File->new for 1..3;
    And if you're using a fairly recent version of Perl, you can even skip those last two steps. Simply using an undef var as if it was a filehandle autovivs as a filehandle! Cool!

    -- Randal L. Schwartz, Perl hacker

      Actually, you can. For example, the following works just fine:

      my $stdout= *STDOUT; print $stdout "Hello\n";
      What you can't do is use a non-string as a hash key and expected it to stay a non-string.

      Even if the above code were switched to use \*INCNT (which I would recommend be done if for no other reason than so that it is more obvious that these are special things that aren't just strings) all that would happen would be that the error message would change to: Can't use string ("GLOB(0xbadd0g)") as a symbol ref while "strict refs" in use To solve the problem you could use Tie::RefHash or you could make the key just "INCNT" and have a value in the hash that is the handle:     $anchors{INCNT}{handle}= \*INCNT; (or an IO::Handle if you prefer).

              - tye (but my friends call me "Tye")
Re: Problems writing to filehandles contained in variables
by graff (Chancellor) on Aug 11, 2002 at 14:36 UTC
    merlyn's advice will get you over this hump, but I still wonder whether you really need to have so many files open at the same time. As I recall from your earlier post, you were looping over hashes, such that at each iteration, one of the hash values would tell you which file to write to on that iteration -- here's a summary of the relevant parts of your code:
    # open three file handles, for NT page, UNIX page, MAIL page foreach my $class ( sort keys %classHash ) { foreach my $status ( sort keys %{$classHash{$class}} ) { if ( $classname{$class} eq "NT" ) ... # set output to NT page if ( $classname{$class} eq "UNIX" ) ... # set output to UNIX page if ( $classname{$class} eq "MAIL" ) ... # set output to MAIL page ... } }
    Now from the looks of that, I think the following would accomplish the same thing, but with only one file open at a time:
    foreach my $page (qw/NT UNIX MAIL/) { # open output html file for this page, print the header, # maybe you open a second file for the html to "#include", # then... foreach my $class (sort grep {$classname{$_} eq $page} keys %classHa +sh ) { foreach my $status (sort keys %{$classHash{$class}}) { # print the page content, etc. } } # print the footer and close this output file(s). }
    This way, you only need to be writing one output file at a time (or, as discussed in the earlier post, two files that combine to create a single web page). There's a lot less system overhead this way, and a lot less code to write (which means less code to have to maintain/fix later). I doubt that the three "sort grep {...} keys %hash" calls are going to cause any noticeable delay.