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

Hello, i am new to the monastery and hope this is the right place for my question. I want to redirect the standard output from a perl function. This function can contain print and system commands. I cannot modify this function! I tried the following:
# this is my sample function i cannot modify sub doIt() { print "blabla\n"; print STDOUT "blabla2\n"; #sample_app is a small script that writes some output to STDOUT an +d STDERR system("sample_app"); } sub execute() { pipe(RDR, WTR); my $pid = fork(); if ($pid == 0) { close STDOUT; open (STDOUT, '>&WTR'); select STDOUT; $| = 1; doIt(); print "child finished\n"; exit; } else { my $buf = ""; while ($buf !~ /child finished/) { $buf .= <RDR>; } sleep 2; print "parent got:\n$buf\n"; } }
if i call execute() i get the output i exspected. $buf contains all the output (including the messages from the system command). Thats fine :) Now i tried this with threads.
use threads; my $t = threads->new(\&execute); $t->join();
Unfortunately this doesnt do what i expected. The output of the system command is written to the shell (and not to my pipe). At the end of the execution $buf only contains "blabla" and "blabla2". Is there a way to get this working in a threaded environment?

Replies are listed 'Best First'.
Re: Redirecting stdout with threads
by kennethk (Abbot) on Mar 14, 2009 at 15:57 UTC
    You are combining multi-process models, which is generally problematic. After creating a thread, you create a pipe, fork, redirect i/o and then call system, which forks again. When that second fork gets called, it seems that it is twinning the STDOUT (I assume you don't care about STDERR since you never redirect) from the main thread rather than your threaded-then-forked line of control. Why are you trying to use threads when you already have a functional code using forks and pipes? You can certain use something like the following code in place of that threading:

    my $pid = fork(); if ($pid == 0) { print "Hello!\n"; } else { execute(); }

    Another possibility is to modify your "can't modify" routine to use backticks in place of system and print the output yourself.

      Thanks for your reply.
      Maybe the example looks a bit weird because i deleted all code that isnt necessary to show my problem.
      I want to use threads cause i feel the communication is easier than with processes. If i create process and a pipe for communication this would result in something like
      while (..) { $tmp = <READ_FROM_OTHER_PROCESS> }
      which blocks one of my processes.
      I tried the ipc forks library instead of threads and it works fine.
        Both methods of IPC work, but it's a very good idea to stick with one consistent approach throughout your code. If you would prefer doing the whole thing with threads, I would refer you to threads on Windows. I posted this question when I was learning threads in Perl, including message passing concepts. Since you are worried about blocking operations, you can maintain your fork/pipe approach and use signals to facilitate a timeout on the connection. There are also a number of IPC-modules on CPAN that may help.