Beefy Boxes and Bandwidth Generously Provided by pair Networks
We don't bite newbies here... much
 
PerlMonks  

Problem with tie *STDIN, 'IO::Scalar', \$text;

by Rudif (Hermit)
on Apr 28, 2001 at 23:50 UTC ( [id://76409]=perlquestion: print w/replies, xml ) Need Help??

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

Ave!

stephen showed us recently here how to capture into a scalar variable the output that a Perl subroutine, such as  Pod::Html::pod2html or  Pod::Select::podselect, writes to  STDOUT :
my $podfile = "some.pod"; my $scalar = ''; tie *STDOUT, 'IO::Scalar', \$scalar; podselect $podfile; # reads form $podfile, writes to STDOUT untie *STDOUT; # now $scalar contains the pod from $podfile
Now I want to achieve the opposite: feed text from a scalar variable to a subroutine that reads from STDIN.
Is it possible, and how could I do it?

I tried this
my $scalar = "some pod text"; tie *STDIN, 'IO::Scalar', \$scalar; pod2html; untie *STDIN; # reads from 'real' STDIN, not from $scalar
but it does not work as I expected. Am I doing something wrong, or is this a limitation of IO::Scalar?

My script below shows the following:
  • tieAngle():   tie *STDIN, 'IO::Scalar', \$text; works as I expected when used with the angle operator  <>
  •  pipePod2html(): a workaround - I can print to a pipe, but only if the subroutine is wrapped in an executable script (pod2html.pl or pod2html.bat on Win32)
  • tiePod2html():  tie *STDIN, 'IO::Scalar', \$text; does not work with pod2html
  • tieOpenDash():  tie *STDIN, 'IO::Scalar', \$text; does not work when I emulate what pod2html does (it opens a file handle to read from STDIN and reads from it).
where 'does not work' means that it does not read from my scalar but from the real STDIN.

My platform is Win2k and perl is 5.6.0, AS build 623.

Rudif
#! perl -w use strict; use Pod::Html; use IO::Scalar; $|++; my $text = <<EOT; =head1 TESTING First line Second line Third line =cut EOT # these work tieAngle(); pipePod2html(); # these do not work tiePod2html(); tieOpenDash(); sub tieAngle { print "\n=1 tieAngle================================== OK\n"; tie *STDIN, 'IO::Scalar', \$text; while (<STDIN>) { print "=1=$_"; } untie *STDIN; } sub pipePod2html { print "\n=pipePod2html================================== OK\n"; open PIPE, "| pod2html"; # works, because there is a pod2html.bat print PIPE $text; } sub tiePod2html { print "\n=tiePod2html================================== NOT OK\n"; tie *STDIN, 'IO::Scalar', \$text; pod2html; untie *STDIN; } sub tieOpenDash { print "\n=3 tieOpenDash================================== NOT OK\n +"; tie *STDIN, 'IO::Scalar', \$text; # similar to what pod2html does: open TSCLR, "<-" or die "can't open <-"; while (<TSCLR>) { print "=3=$_"; } close TSCLR; untie *STDIN; } __END__

Replies are listed 'Best First'.
Re: Problem with tie *STDIN, 'IO::Scalar', \$text;
by chromatic (Archbishop) on Apr 29, 2001 at 01:45 UTC
    The perldoc perltie has some good examples of how to do this. I'd start with Tie::Handle, subclass it, and go from there.

    You could probably get by with overloading TIEHANDLE() and READLINE(), though your needs will determine that. I can provide code if you really need some, but it's pretty plain in perltie.

    Update: Perhaps I've not made myself clear. Yes, I've found myself in exactly the same situation you describe. Trying to integrate HTTP::Daemon with CGI is a hassle -- HTTP::Daemon produces a HTTP::Response object that allows you to get at posted data via the content() method (I may have the names slightly wrong, but the idea is the same). CGI reads from STDIN or another filehandle.

    The solution was to make a tied filehandle, pass it to the CGI.pm constructor. This let me intercept the READ and READLINE calls from CGI.pm to the filehandle, pulling data out of content(), manipulate them as desired, and pass them along.

    As far as HTTP::Response knew, I was doing normal stuff. As far as CGI.pm knew, it was reading from a normal filehandle.

    Yes, it would be nice if they knew how to play together. At the cost of writing 30 lines of glue code, I faked them out.

    That is why I brought it up -- it will let you do just what you describe.

      Hi chromatic

      At some other time I will probably follow your advice and learn how to subclass Tie::Handle to solve some specific problem.

      At this stage I am raising what to me looks like a general perl programming question:

      Some popular perl modules export subroutines that read text from specified input file(s), defaulting to STDIN, transform the text, and deliver the result to a specified output file, defaulting to STDOUT. This is fine in many applications.

      Two subroutines that behave like this, and that I'm interested in right now are
      Pod::Html::pod2html Pod::Select::podselect

      However, I want to call these subroutines in a context where the input text or output text, or both, are in my script's scalar variables. This lets me do some other processing before calling one of these subroutines, and some more after the call.

      I would like to know if other monks have had this of requirement or wish.

      While I can easily solve my problems by using temporary files, out of curiosity I started looking for a more perlish solution. Following stephen's advice, I was able to redirect podselect STDOUT into a IO::Scalar tied to a scalar variable (see the first snippet in my post above). However, my attempt to use a similar redirection on STDIN to pod2html failed. I will try to dig deeper to try to understand why does it work when it works, and why not when not.

      Rudif
Re: Problem with tie *STDIN, 'IO::Scalar', \$text;
by LunaticLeo (Scribe) on Apr 29, 2001 at 08:38 UTC
    First, I gotta take issue with your unnessesary and obfusicating use of subroutines. If a subroutine takes no arguments and returns nothing, don't use it. Really, you are just creating a block that has a name. All the real work is done implicitly. Almost always implicit modification of globals is a VERY BAD IDEA.

    As to the real issue...
    You are tie'ing a scalar to the global symbol for the STDIN filehandle. Then you try to open the real STDIN using a trick perl provides. Even if your method could work, which it won't, It is a BAD IDEA(tm) to open a file handle twice.

    You should be trying to dup(2) the filehandle since it is already open. But that won't work either.

    The tie() doesn't create a real filehandle in the OS sense of the term. It just lets you intercept calls to the mid-level Perl abstraction of a file handle. This is fine because the Perl abstraction is access via a known API. open(FH, "<-") is trying to open the REAL filehandle. Further, the Perl FileHandle TIE API doesn't provide functions for open() or dup() (interestingly it does provide CLOSE).

    So what is a perl-coder supposed to do? Well I don't know cuz you didn't show the real problem you are trying to solve and the assosiated code.

    Where you have:
    open TSCLR, "<-" or die "can't open <-";
    you could replace it with:
    local *TSCLR = \*STDIN;
    This works at the name space level.

    If you went all the way using the OO Perl file handle modules, you could have code that works this way:

    use IO::File (); use IO::Scalar (); sub do_stuff_with_a_fh { my ($fh) = @_; ...do stuff... } my $str = "this is my string"; my $fh_scalar = IO::Scalar->new(\$str) or die "failed to create fh from str: $!"; do_stuff_with_a_fh($fh_scalar); my $fh_stdin_duped = IO::File->new("<&STDIN") or die "failed to dup stdin: $!"; do_stuff_with_a_fh($fh_sdtin_duped);
    The above code style is the reason for the whole IO:: heirarchy (from my perspective). You can pass file handles into subroutines easily, and if those sub routines use file handles in either the OO-way or with <$fh> old-perl way it all works out as you expect.

    Please post an update, and we can help you further.

    P.S. Yes that pod2html() function from Pod::Html uses implicit inputs and outputs. See, that proves my point about subroutines with no inputs and ouputs. It restricts the flexability. It could have been pod2html($fh_pod_input, $fh_html_output).

      Well I don't know cuz you didn't show the real problem you are trying to solve and the assosiated code.

      pod2html() function from Pod::Html uses implicit inputs and outputs.
      It could have been pod2html($fh_pod_input, $fh_html_output).


      But it is not, and hence my problem.
      I want to feed text from a scalar variable to a subroutine that reads from STDIN. Specifically, I want to convert some pod text from a scalar variable to a html file, using Pod::Html::pod2html.

      This here does the job, but it uses a script pod2html.pl that wraps the sub Pod::Html::pod2html:
      sub string2Html { my ($htmlfilename, $podtext) = @_; open PIPE, "| pod2html --outfile $htmlfilename"; # works, because there is a pod2html.bat print PIPE $podString; close PIPE; }
      I would like to replace it by something like this, not using a separate script and pipes or temporary files:
      sub string2Html { my ($htmlfilename, $podtext) = @_; ### some code that causes pod2html to read from scalar \$podtext; ### when it wants to read from STDIN Pod::Html::pod2html "--outfile $htmlfilename"; ### code to wrap up }
      I hope that this clarifies what I am trying to solve.

      Rudif
        Thanks for the clarification. I should have read your original post more thoroughtly.

        I tried all my tricks to fake out &Pod::Html::pod2html, but I can't make it work without resorting to pipes.

        If you wish to resort to pipes, the following Worked For Me(tm).

        use strict; use IO::File; use IO::Pipe; use Pod::Html qw( &pod2html ); # I only use $fh to get a pod string, It is not part # of the solution. my $fh = IO::File->new("<foo.pod") or die "filaed to open foo.pod: $!"; $fh->input_record_separator( undef ); my ($str); $str = $fh->getline(); $fh->close; # Got the pod string in $str my $pipe = IO::Pipe->new() or die "failed to create pipe: $!"; my ($pid,$fd); if ( $pid = fork() ) { #parent open(TMPSTDIN, "<&STDIN") or die "failed to dup stdin to tmp: $!"; $pipe->reader(); $fd = $pipe->fileno; open(STDIN, "<&=$fd") or die "failed to dup \$fd to STDIN: $!"; pod2html(); open(STDIN, "<&TMPSTDIN") or die "failed to restore dup'ed stdin: $!"; } else { #child $pipe->writer(); $pipe->print($str); $pipe->close(); exit 0; } $pipe->close(); exit 0;

        I think this will work satifactorally even on the Win32 port of Perl. Win32 doesn't really fork(), it just creates a thread. But I restore the original STDIN and the child dies pretty quickly.

      Sorry this is sorta off on a tangent, but why the hostile attitude towards subroutines?

      Obviously, implicit modification of globals CAN be a Bad Thing™ if it's done in such a way that it's hard to follow, but if you're writing a small script,and pressed for time, and you're careful to document what you're doing, it's a timesaver. What other problems does it cause that I don't know about?

      I've always thought of subroutines as just that - named blocks - and to me, that makes them a handy way to document or re-use code, even if they don't take args or return anything explicitly.

      Have I overlooked some major diabolical evil that subroutines are capable of without knowing it? Or is my perl style just lacking for want of experience?
      "All generalizations are incorrect, including this one." Anonymous
        I think you have overlooked some major diabolical evil :).

        Named blocks, as opposed to functions, HIDE the external effects of the block from the reader. Using the name of the block as some kind of documentation feature is a poor choice.

        Even if you want your subroutine to affect globals, you can pass globals in and return new values for globals. This "documents" your named block even better; explicitly telling the reader what this chunk of code depends on and what it changes.

        $GLOBAL1 = do_stuff($GLOBAL0, $GLOBAL1);

        is MUCH better than:

        ... set globals ... ... do_stuff; ... ... use modified globals

        I have had to read code like this (and it is very hard):

        &read_input; &calculate_this; &calculate_that; &write_output; # some to files others to databases # all in one function. sub calculate_this { ... } sub read_input { ... } sub write_output { ... } sub calculate_that { ... }

        This was horrible. I was tempted to just remove the "sub" lines, but then I noticed the subs were not declared in the order they were used. As I paid more attention, some variables inside the subs were my()'ed but had the same name as globals.

        We had to audit this code to make sure it was calculating money dispusements correctly. How can you audit this code?

        It was painfull to read, impossible to audit, and such a rats nest it was difficult to modify. I lay most of the blame at the fact that it was using globals, locals, and named blocks. Writing functions force a certain amount of structure. Even if the structure is lame it is emminently more readable and auditable.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://76409]
Approved by root
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others avoiding work at the Monastery: (5)
As of 2024-04-18 20:33 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found