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

Great monks of Perl,

I have to port to Windows, and in Perl, a set of ksh scripts. These scripts use redirections to one unique error file for different commands. That is they do things like :

cmd_uno >> cmd_uno_out 2>> general_err & cmd_dos >> cmd_dos_out 2>> general_err &
Every command launched is a different script or binary. They can be system commands or written by others persons, some may run in the background for a long time. The number and nature of these command is not controlled by me and will vary. They should run at the same time.

Is this single file redirection not at all possible with windows ? I tried several approaches :
- redirection through a cmd shell
- passing the filename as a parameter before spawning - passing already opened IO::File filehandles

Everytime I get a "File already used by another process" error. It seems strange that something so easy with Unix is impossible with windows, so maybe i'm missing something ? Is this a well known problem ? What are the workarounds ? Is it done with other languages ?

Another problem is that i work with Perl 5.6 and so i do not have Win32::Job. This module allows to redirect STDERR and STDOUT of the spawned process to files specified at launch time. Since i cannot do this I have to impose that the commands are either doing the redirections themselves or launched through a cmd shell that does the redirections for them.

Add these two problems and you see that i have to :

I must add that i cannot use other modules than pure perl ones or the ones that are in the standard 5.6 distribution

I already have some code running, but maybe someone here can give me ideas on trying to make this as clean as possible. What do you think ?

Replies are listed 'Best First'.
Re: Win32 multiple process and single file redirection
by pelagic (Priest) on Jan 21, 2005 at 10:30 UTC
    As STDOUT is obviously cmd-specific you just want to concentrate STDERR to one common file.
    This looks like a good example for one of the many Logfile Modules you could find on CPAN.

    pelagic
      Hmm, OK. Here's my first try with Log::Dispatch . It fails : the final log file holds a mixed content of only one script and the end of the launcher logs .

      I will try with Log::Agent ("dispatch" seems aimed at the opposite, that is log one same things to different places, err ... ).

    • t1.pl ( launches t2.pl and t3.pl , they all try to log to the same file ) :
      #!perl package MyProjectPackage ; use strict ; use warnings ; use Win32::Process ; my $MLIB ; BEGIN { $MLIB = "c:/zlr" ; } use lib ( $MLIB . "/lib/" , $MLIB . "/lib/Perl/" ) ; use Log::Dispatch ; use Log::Dispatch::File ; my $dispatcher = Log::Dispatch->new ; my $MSOFT = "c:/zlr/script/test" ; my $call_hProcess = "" ; my $call_Resultat = 0 ; my $i = 0 ; $dispatcher->add( Log::Dispatch::File->new( name => 'MyProjectErrT1', min_level => 'info', filename => "$MSOFT/ErrGeneral.txt" ) ); $dispatcher->log( level => 'info', message => 'Start of log' ); $call_Resultat = Win32::Process::Create($call_hProcess, "c:/Perl/bin/Perl.exe", "perl t2.pl", 0, IDLE_PRIORITY_CLASS, "." ) ; $call_Resultat = Win32::Process::Create($call_hProcess, "c:/Perl/bin/Perl.exe", "perl t3.pl", 0, IDLE_PRIORITY_CLASS, "." ) ; while ( $i < 20 ) { sleep 10 ; $dispatcher->log( level => 'info', message => "Launcher : $i\n" ); $i++ ; }
    • Here's t2.pl , t3.pl is the same :
      #!perl # Pragmas package MyProjectPackage ; use strict ; use warnings ; use Win32::Process ; my $MLIB ; BEGIN { $MLIB = "c:/zlr" ; } use lib ( $MLIB . "/lib/" , $MLIB . "/lib/Perl/" ) ; use Log::Dispatch ; use Log::Dispatch::File ; my $dispatcher = Log::Dispatch->new ; my $MSOFT = "c:/zlr/script/test" ; my $call_hProcess = "" ; my $call_Resultat = 0 ; my $i = 0 ; $dispatcher->add( Log::Dispatch::File->new( name => 'MyProjectErrT2', min_level => 'info', filename => "$MSOFT/ErrGeneral.txt" ) ); $dispatcher->log( level => 'info', message => 'Start of t2' ); while ( $i < 20 ) { sleep 10 ; $dispatcher->log( level => 'info', message => "t2.pl : $i\n" ); $i++ ; }
        ZlR
        Sorry to hear that! I actually do not know personally any of those modules because my firm decided to use something homegrown (that I may unfortunately not provide to you). I know that e.g. Log::Log4perl is very complete and that you could ask the author (Mike Schilli <m@perlmeister.com>) questions in case you had problems with it. But on the other hand this module might be overkill for a relatively simple problem.

        pelagic
        You can try File::Log

        With this package, differents process can log in the same file.

      • t1.pl ( launches t2.pl and t3.pl ) :
        use strict ; use warnings ; use Win32::Process ; use File::Log; my $call_hProcess = "" ; my $call_Resultat = 0 ; $call_Resultat = Win32::Process::Create($call_hProcess, "c:/Perl/bin/Perl.exe", "perl t2.pl", 0, IDLE_PRIORITY_CLASS, "." ) ; $call_Resultat = Win32::Process::Create($call_hProcess, "c:/Perl/bin/Perl.exe", "perl t3.pl", 0, IDLE_PRIORITY_CLASS, "." ) ;
      • Here's t2.pl , t3.pl
        use strict ; use warnings ; use File::Log; # Log INFORMATIONAL or worse to a file my $log = File::Log->new({ debug => 4, logFileName => 'app.log', logFileMode => '>>', dateTimeStamp => 0, stderrRedirect => 0, defaultFile => 1, logFileDateTime => 0, appName => 'MyApp', PIDstamp => 1, storeExpText => 0, }); for ( my $i=0; $i <= 50; $i++ ) { sleep 2 ; $log->msg(4, "t2.pl : $i\n"); }