http://qs1969.pair.com?node_id=11137772


in reply to Assigning printf to a variable

Just as an aside (very tangentially related), if you've got (say) external code in a module you can't easily change with printfs you need to capture you could always locally set STDOUT to a filehandle opened on a scalar ref.

#+begin_src perl :results output drawer use strict; use 5.026; my $output; do { open( my $fh, q{>}, \$output ) or die "Problem redirecting to scal +ar: $!\n"; local( *STDOUT ) = $fh; ## pretend this bit is in some 3rd party module you can't modify printf( "This should go %s", qq{elsewhere} ); }; say qq{\$output is '$output'}; #+end_src #+RESULTS: :results: $output is 'This should go elsewhere' :end:

The cake is a lie.
The cake is a lie.
The cake is a lie.

Replies are listed 'Best First'.
Re^2: Assigning printf to a variable
by haukex (Archbishop) on Oct 20, 2021 at 08:04 UTC
    local( *STDOUT ) = $fh;

    Or select, though in this example local does have the advantage that it is restored after the block ends.

    But in general, when wanting to capture STDOUT/ERR, I'd recommend Capture::Tiny.

      But in general, when wanting to capture STDOUT/ERR, I'd recommend Capture::Tiny.

      But can Capture::Tiny be used to capture the STDOUT/ERR of the same script that's running?

      Answering my own question, I see the tee and related functions, but that requires that you put the whole script (or at least the parts you want to capture) inside the block that tee is calling. Would there be an equivalent to my Re^2: Errors uncaught by CGI::Carp solution, which captured the STDOUT for the whole script, then did two things with that output during the END (process to one location and dump to the old STDOUT)? Though I guess that use Capture::Tiny; my @capture = tee { ...whole script here... }; process(@capture); isn't that different from BEGIN { duplicate } END { process(...) } ... whole script here .... I guess I'm just trying to see what other ways are possible in the TIMTOWTDI: so would tee {original script contents} be the canonical way with Capture::Tiny, or is there something that involves less wrapping?

      (Sorry for the rambling, self-responding post)

Re^2: Assigning printf to a variable
by Bod (Parson) on Oct 20, 2021 at 12:19 UTC

    pryrt very helpfully provided a snippet to redirect STDOUT over on Re^2: Errors uncaught by CGI::Carp

    There, the >& file operator was used. I had come across this before but didn't properly understand it. So I looked it up and thought I understood. But, from my understanding I would have thought that Fletch's example should use open( my $fh, q{>&}, \$output ) or die "Problem redirecting to scalar: $!\n"; instead of the plain q{>}

    What have I missed?

      Thanks for the mention. I'm not an expert on such things; I just learned more about it than I ever thought I would when I was trying to create tests for Games::Literati -- enough that I can muddle through it when I want to do in-memory filehandles and STDOUT duping. But I did have to muddle some before I got that example working.

      Your open( my $fh, q{>&}, \$output ) or die "Problem redirecting to scalar: $!\n"; snippet is combining two concepts: there's the writing to an in-memory filehandle, which is the \$output portion; and there is the "duping" (duplicating) filehandles, which is the q{>&}. You can open a filehandle into the scalar variable like Fletch did without requiring the duping. In Re^2: Errors uncaught by CGI::Carp, I used duping on STDOUT because I had to grab the old version of STDOUT and then replace STDOUT with a new handle. If you look at that code again, you'll see the duping-& was only used on the opens that were dealing with STDOUT, not on the open for the in-memory filehandle. You'll want to keep the two concepts separate in your mind, otherwise you'll make things even more confusing for yourself (and will have a harder time muddling through than I did.)