strange warning when open file

G

George Mpouras

Do you have any idea how to fix the warning
Name "main::FH" used only once: possible typo at C:\work\Files\test.pl
at the following code ?



#!/usr/bin/perl
use strict;
use warnings;

my $output_dir = '/tmp';
my %File = (
'mc' => { '12' => '', '13' => '', },
'mp' => { '12' => '', '13' => '', },
);


foreach my $key1 (keys %File)
{
foreach my $key2 (keys %{$File{$key1}})
{
$File{$key1}{$key2} = { *FH => \"$key1,$key2" , 'FILE' =>
"$output_dir/$key1,$key2" };
open $File{$key1}{$key2}{'FH'}, '>', $File{$key1}{$key2}{'FILE'} or die
"Could not open file \"$File{$key1}{$key2}{'FILE'}\" because \"$^E\"\n";
}
}

# Write something
print {$File{nc}{12}{FH}} "test nc 12\n";
print {$File{alp}{14}{FH}} "test alp 14\n";

# Close the filehandlers
foreach my $key1 (keys %File) {
foreach my $key2 (keys %{$File{$key1}}) {
close $File{$key1}{$key2}{'FH'} or die "Could not close file
\"$File{$key1}{$key2}{'FILE'}\" because \"$^E\"\n" }}
 
G

George Mpouras

sorry for the tipo

print {$File{nc}{12}{FH}} "test nc 12\n";
print {$File{alp}{14}{FH}} "test alp 14\n";

should be

print {$File{mc}{12}{FH}} "test mc 12\n";
print {$File{mp}{13}{FH}} "test mp 13\n";

Of course the warning issue remains
 
P

Peter Makholm

George Mpouras said:
Do you have any idea how to fix the warning
Name "main::FH" used only once: possible typo at C:\work\Files\test.pl
at the following code ?

And it doesn't tell you a line number where it encounters the name
main::FH?

My perl does, it is on line 16.
foreach my $key2 (keys %{$File{$key1}})
{
$File{$key1}{$key2} = { *FH => \"$key1,$key2" , 'FILE' => "$output_dir/$key1,$key2" };

i.e. this line.

You are using a variable called *FH here and nowhere else in your
script. Which is what perl tries to warn you about.

Variables using the * sigil is a class of variables called
typeglobs. Just like variables starting with the $ sigil ara scalars and
variables starting with the @ sigil is arrays.

Typeglobs are an internal type used by perl to hold symbol table
entries. Previously it was used for references and filehandles, but this
usage is deprecated. You can read more about typeglobs in the perldata
manual page.


What you probably meant was the string 'FH'. You can do this by either
just removing the * and use the stringification feature of the =>
operator or explicitly use a string like you do for the FILE field.

//Makholm
 
R

Rainer Weikusat

[...]
Variables using the * sigil is a class of variables called
typeglobs. Just like variables starting with the $ sigil ara scalars and
variables starting with the @ sigil is arrays.

Typeglobs are an internal type used by perl to hold symbol table
entries. Previously it was used for references and filehandles, but this
usage is deprecated.

The short way to describe what 'a typeglob' actually is would be 'what
a symbol is in Lisp': An object which aggregates a number of 'slots'
used by the language for different purposes and which is usually
stored in a symbol table where it can be found by doing an 'ordinary'
name lookup which thus provides a way to employ the functionality
provided by the 'slots' by name. For a perl typeglob, the
commonly-used slots would be (assuming the name pointing to the typeglob was
richard) $richard, the slot used for scalars, %richard, the hash slot,
@richard, the array slot and lastly, the &richard slot for
subroutines. The *-notation can be used to refer to the typeglob
itself, eg, in the case of a named I/O handle (another typeglob slot),
the syntax \*richard could be used to pass a reference to that
(actually, to the typeglob itself) to a subroutine (*richard{IO} could
be used to create a reference to the 'thing' in the I/O handle slot).

Another use of *richard would be to assign a reference to something to
one of the slots, eg

*richard = sub { return 'richard'; }

creates an anonymous subroutine returning 'richard' and assigns a reference
to that to the subroutine slot of the glob associated with the name
richard. Effectively, this binds a name to this anonymous subroutine
(or vice versa). This is also the way how importing subroutines from
other modules works.

The reference to references refers to a deficiency of some ancient
programming language someone reportedly used somewhere about twenty
years ago. It ought to be forgotten nowadays. Likewise, in ancient
versions of Perl5, the only way to create an I/O handle was to refer
to a typeglob via the symbol table of the current package, eg

open(IN, '</etc/passwd')

will open the file /etc/passwd for reading and deposit the
corresponding I/O handle in the I/O handle slot of the typeglob
associated with the name IN. Nowadays, it is usually more convenient
to use an 'undefined' scalar variable for this purpose: This cause an
anonymous typeglob to be created and assigned to this variable
(actually, a reference to it is assigned). This has the nice
additional benefit that the filehandle will be closed automatically
when the scalar variable goes out of scope (be warned that this is
another highly blasphemous heresy because 'mighty sheep' garbage
collectors don't provide this functionality ...)
 
G

George Mpouras

The reference to references refers to a deficiency of some ancient
programming language someone reportedly used somewhere about twenty
years ago. It ought to be forgotten nowadays. Likewise, in ancient
versions of Perl5, the only way to create an I/O handle was to refer
to a typeglob via the symbol table of the current package, eg

open(IN, '</etc/passwd')



As you can see Rainer the code is dynamic. In reality the hash is also
dynamic, so I will end up with hundrends of open fileshat they will get
written also dynamic using the hash keys as filehanl pointers
So using a simple keyword like IN is not good enough.
I have to use the full path of hash keys as a unique FH identifier, othelse
all writings will be wrong.
This is why i used the * ...
 
W

Willem

George Mpouras wrote:
) As you can see Rainer the code is dynamic. In reality the hash is also
) dynamic, so I will end up with hundrends of open fileshat they will get
) written also dynamic using the hash keys as filehanl pointers
) So using a simple keyword like IN is not good enough.
) I have to use the full path of hash keys as a unique FH identifier, othelse
) all writings will be wrong.
) This is why i used the * ...

That's not how filehandle/fileglob references work.

Try this:

use strict;
use warnings;

my %x;
$x{one}{FILE} = "testfile-one.txt";
$x{two}{FILE} = "testfile-two.txt";
for my $xx (keys %x) {
open $x{$xx}{FH}, '>', $x{$xx}{FILE} or die "Failed to open: $_\n";
}
for my $xx (keys %x) {
print { $x{$xx}{$FH} } "Text for $xx\n" or die "Failed to print: $_\n";
}
for my $xx (keys %x) {
close $x{$xx}{FH} or die "Failed to close: $_\n";
}

And also try to figure out why it works.


SaSW, Willem
--
Disclaimer: I am in no way responsible for any of the statements
made in the above text. For all I know I might be
drugged or something..
No I'm not paranoid. You all think I'm paranoid, don't you !
#EOT
 
G

George Mpouras

Actually thats what I did. It works ( I wonder why ... )


# Open Files
foreach my $key1 (keys %File)
{
foreach my $key2 (keys %{$File{$key1}})
{
$File{$key1}{$key2} = { FH => undef , 'FILE' =>
"$output_dir/$key1,$key2" };
open $File{$key1}{$key2}{'FH'}, '>', $File{$key1}{$key2}{'FILE'} or die
"Could not open file \"$File{$key1}{$key2}{'FILE'}\" because \"$^E\"\n";
}
}
 
R

Rainer Weikusat

George Mpouras said:
Actually thats what I did. It works ( I wonder why ... )


# Open Files
foreach my $key1 (keys %File)
{
foreach my $key2 (keys %{$File{$key1}})
{
$File{$key1}{$key2} = { FH => undef ,

The assignment is not needed: The first time the other code uses
$File{$key1}{$key2}{FH} in a rvalue context which needs a reference
to a 'value' of some type, a suitable value will be created and a
reference to it assigned to the hitherto 'undefined' location
(so-called 'auto-vivification'). One of these contexts is the first
argument to an open call which is either supposed to be the name of a
typeglob or a reference to a type glob, cf the following example:

--------------
open($passwd, '<', '/etc/passwd');
print($passwd, "\n");
--------------

which (on a system where a file named /etc/passwd exists ;-) prints
the reference to the anonymous glob which was assigned to $passwd when
autovivification took place.

As Peter Makholm already wrote earlier: The error in your original
code was that you wrote

*FH => undef

in a hash definition. The =>-operator automaically quotes its left
argument if "it begins with a letter or underscore and is composed
only of letters, digits and underscores" (=> perlop(1)). This is not
the case here, hence *FH is evaluated as an expression. The value of
this expression is the typeglob currently associated with the name FH
in the symbol table of the current package, which causes the warning
to be printed. It will probably also not do what you wanted wrt
creating a hash entry because *FH stringfies to the fully-qualified
name of the referenced typeglob, cf

-----------
my $h = { *FH => 'FH' };

print $h->{*FH}, "\n";

package haha;

print $h->{*FH}, "\n";

print("The solution: ", keys(%$h), "\n");
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,764
Messages
2,569,567
Members
45,041
Latest member
RomeoFarnh

Latest Threads

Top