Download Reference Manual
The Developer's Library for D
About Wiki Forums Source Search Contact

Reading output from child process

Moderators: kris

Posted: 05/29/07 14:05:10

It seems that LineIterator? reads output from child process incorrectly. DMD v.1.014, Tango 0.98, WinXP-SP2

There is extract from my program:

module runner;

import tango.core.Exception;

import tango.io.Conduit;
import tango.io.Console;
import tango.io.Stdout;

import tango.sys.Environment;
import tango.sys.Process;

import tango.text.Util;
import tango.text.stream.LineIterator;

// Exception for a case when process finishes with non-zero status.
// Exception stores content of STDERR of terminated process.
class NonZeroProcessStatusException : TracedException
  {
    this( char[] msg, char[][] stderr )
      {
        super( msg );
        stderr_ = stderr.dup;
      }

    char[][]
    stderr() { return stderr_; }

  private :
    char[][] stderr_;
  }

V[K]
merge(K, V)( ref V[K] receiver, in V[K] source )
  {
    foreach( k, v; source )
      receiver[ k ] = v;
    return receiver;
  }

// Launches specified process and returns content of its STDOUT.
// Throws NonZeroProcessStatusException if process finishes with
// non-zero status.
char[][]
launchProcess(
    char[][] commandLine,
    char[][char[]] env,
    char[] workingPath = null )
  {
version(USE_LINE_ITERATOR)
{
    char[][]
    readProcessStream( Conduit conduit )
      {
        char[][] r;
        foreach( line; new LineIterator!(char)( conduit ) )
          r ~= line;
        return r;
      }
}
else
{

    char[][]
    readProcessStream( Conduit conduit )
      {
        char[] tmp;
        char[ 256 ] buf;
        while( conduit.isReadable )
          {
            uint count = conduit.read( buf );
            if( cast(uint)-1 != count )
              tmp ~= buf[ 0..count ];
            else
              break;
          }

        return splitLines( tmp );
      }

}

    typeof(env) fullEnv;
    merge( fullEnv, Environment.get );
    merge( fullEnv, env );

    auto p = new Process( commandLine, fullEnv );

    if( workingPath )
      p.workDir = workingPath;

    p.execute();

    auto output = readProcessStream( p.stdout );
    auto stderr = readProcessStream( p.stderr );

    auto launchResult = p.wait();
    if( 0 != launchResult.status )
      throw new NonZeroProcessStatusException(
          "Process '" ~ p.toUtf8 ~ "' non-zero status: '" ~
          launchResult.toUtf8 ~ "'",
          stderr );

    return output;
  }

int
main( char[][] args )
  {
    try
      {
        char[][char[]] dummyEnv;

        auto output = launchProcess( args[ 1..$ ], dummyEnv );
        foreach( l; output )
          Stdout( "'" ~ l ~ "'" ).newline;
      }
    catch( NonZeroProcessStatusException x )
      {
        Cerr( "Exception: " ~ x.toUtf8 ~ "\nStderr:\n" );
        foreach( line; x.stderr )
          Cerr( "\t" ~ line ~ "\n" );
        Cerr.flush;

        return 1;
      }
    catch( Exception x )
      {
        Cerr( "*** Exception caught:\n\t" )( x ).newline.flush;
        return 2;
      }

    return 0;
  }

// vim:ts=2:sts=2:sw=2:expandtab

I compile it as:

dmd -ofrunner.exe -version=USE_LINE_ITERATOR runner.d tango.lib

Here is sample program which simple outputs his arguments:

import tango.io.Stdout;

int
main( char[][] args )
  {
    foreach( a; args[ 1..$ ] )
      Stdout( a ).newline;
    return 0;
  }

// vim:ts=2:sts=2:sw=2:expandtab

When I run runner.exe with command line:

runner.exe printer.exe "      main     " "----------------" " 20006"

I have the following output:

'---------------'
'----------------'
' 20006'

But if I compile runner.d as:

dmd -ofrunner.exe runner.d tango.lib

and run it with the same command line I have correct output:

'      main     '
'----------------'
' 20006'

And yet another question: is it right that tango.io.Conduit.read returns -1 at the end of stream? (It seems that tango.io.Conduit.isReadable returns true always in my sample).

Regards, Yauheni Akhotnikau

Author Message

Posted: 06/02/07 07:16:23

that is very odd ~ thanks for posting this. Conduit.read() return IConduit.Eof at end of input (which is uint.max)

Posted: 06/13/07 02:12:15

oh, wait!

LineIterator? returns a slice of the input ... if you're gonna save the result, you must .dup it or otherwise save it somewhere. It does this to avoid lots of heap activity where the results might simply be thrown away after usage. A lot of Tango operates in this manner, avoiding heap activity where possible.