1

I am pulling out my hair on using the file handle returned by select.

The documentation about select reads:

  • select
    Returns the currently selected filehandle.

I have a piece of code, that prints some data and usually is executed without any re-direction. But there is one use case, where select is used to re-direct the print output to a file.

In this piece of code, I need to use the current selected file handle. I tried the following code fragment:

my $fh = select;
print $fh "test\n";

I wrote a short test program to demonstrate my problem:

#!/usr/bin/perl

use strict;
use warnings;

sub test
{
  my $fh=select;
  print $fh "@_\n";
}

my $oldfh;

# this works :-)
open my $test1, "> test1.txt";
$oldfh = select $test1;
test("test1");
close select $oldfh if defined $oldfh;

#this doesn't work. :-(
# Can't use string ("main::TEST2") as a symbol ref while "strict refs" in use
open TEST2,">test2.txt";
$oldfh = select TEST2;
test("test2");
close select $oldfh if defined $oldfh;

#this doesn't work, too. :-(
# gives Can't use string ("main::STDOUT") as a symbol ref while "strict refs" in use at
test("test");

It seems, that select is not returning a reference to the file handle but a string containing the name of the file handle.

What do I have to do to always get a usable file handle from select's return value?

P.S. I need to pass this file handle as OutputFile to XML::Simple's XMLout().

Mario Klebsch
  • 361
  • 2
  • 12

3 Answers3

3

The point of select is you don't need to specify the handle at all, since it's the default one.

sub test {
    print "@_\n";
}

That's also the reason why select isn't recommended: it introduces global state which is hard to track and debug.

choroba
  • 231,213
  • 25
  • 204
  • 289
3

Just use

print XMLout(...); 

It seems, that select is not returning a reference to the file handle but a string containing the name of the file handle.

It can indeed return a plain ordinary string.

>perl -MDevel::Peek -E"Dump(select())"
SV = PV(0x6cbe38) at 0x260e850
  REFCNT = 1
  FLAGS = (PADTMP,POK,pPOK)
  PV = 0x261ce48 "main::STDOUT"\0
  CUR = 12
  LEN = 24

But that's perfectly acceptable as a file handle to Perl. There are four things that Perl accepts as file handles:

  • A reference to an IO object.

    >perl -e"my $fh = *STDOUT{IO}; CORE::say($fh 'foo');"
    foo
    
  • A glob that contains a reference to an IO object.

    >perl -e"my $fh = *STDOUT; CORE::say($fh 'foo');"
    foo
    
  • A reference to a glob that contains a reference to an IO object.

    >perl -e"my $fh = \*STDOUT; CORE::say($fh 'foo');"
    foo
    
  • A "symbolic reference" to a glob that contains a reference to an IO object.

    >perl -e"my $fh = 'STDOUT'; CORE::say($fh 'foo');"
    foo
    

    This type doesn't work under strict refs, though.

    >perl -Mstrict -e"my $fh = 'STDOUT'; CORE::say($fh 'foo');"
    Can't use string ("STDOUT") as a symbol ref while "strict refs" in use at -e line 1.
    

What do I have to do to always get a usable file handle from select's return value?

As demonstrated above, it already returns a perfectly usable file handle. If XMLout doesn't support it, then it's a bug in XMLout. You could work around it as follows:

my $fh = select();
if (!ref($fh) && ref(\$fh) ne 'GLOB') {
   no strict qw( refs );
   $fh = \*$fh;
}

This can also be used to make the handle usable in a strict environment


As bad as XML::Simple is at reading XML, it's a million times worse at generating it. See Why is XML::Simple Discouraged?.

Consider XML::LibXML or XML::Twig if you're modifying XML.

Consider XML::Writer if you're generating XML.

ikegami
  • 367,544
  • 15
  • 269
  • 518
  • Your code did the trick! The magic seems to be not using strict. My original code also works, when I comment out `use strict;`. Thank you very much. – Mario Klebsch Oct 16 '18 at 08:45
0

First of all, you shouldn't use XML::Simple , because it will need lots of work to make sure that your output will generate consistent XML. At least make sure you're using the appropriate ForceArray parameters.

Instead of doing filehandle shenanigans, why don't you use the simpler

print XMLout($data, %options);

... instead of trying to pass a default filehandle around?

Corion
  • 3,855
  • 1
  • 17
  • 27
  • Using XML::Simple was not my choice. It is used in old code I need to maintain. The reason, why I want to use the OutputFile option is, that I do not want to store the XML output in a variable, when it can be directly written to the file handle, where it needs to go. – Mario Klebsch Oct 15 '18 at 15:12