Tyrrrz / CliWrap

Library for running command-line processes
MIT License
4.32k stars 264 forks source link

Problem with stdOut #213

Closed Kiryuumaru closed 7 months ago

Kiryuumaru commented 1 year ago

Version

3.6.4

Details

Using linux. I have a python code that prints a string every seconds for 10 sconds. I want to read the output every seconds using this code:

await foreach (var cmdEvent in Cli.Wrap("python").WithArguments("test.py Joe").ListenAsync())
{
    switch (cmdEvent)
    {
        case StartedCommandEvent started:
            Console.WriteLine($"Process started; ID: {started.ProcessId}");
            break;
        case StandardOutputCommandEvent stdOut:
            Console.WriteLine($"Out> {stdOut.Text}");
            break;
        case StandardErrorCommandEvent stdErr:
            Console.WriteLine($"Err> {stdErr.Text}");
            break;
        case ExitedCommandEvent exited:
            Console.WriteLine($"Process exited; Code: {exited.ExitCode}");
            break;
    }
}

The python code test.py

import time
import sys

def print_hi(name):
    for _ in range(10):
        print(f'Hi, {name}')
        time.sleep(1)

if __name__ == '__main__':
    print_hi(sys.argv[0])

But this only prints everything after 10 seconds. Not every seconds. What might be the problem with this snippet.

Steps to reproduce

Use linux with python

Tyrrrz commented 1 year ago

I don't know how print works in Python, but do you need to flush the data by any chance?

Kiryuumaru commented 1 year ago

In python theres no need to flush when printing. This code will run normally when I execute it directly with python test.py "Joe". This will print every seconds Hi, Joe x10 .

Tyrrrz commented 1 year ago

Does print produce a newline at the end? ListenAsync() looks for line breaks.

Kiryuumaru commented 1 year ago

Yes. I also tried to manually add \n to the print

Tyrrrz commented 1 year ago

Not sure then

Tyrrrz commented 1 year ago

Could be related to #214

Tyrrrz commented 10 months ago

Were you able to work around this issue? Did you try using the raw Process class and see if it behaves differently with OutputDataReceived?

WojciechNagorski commented 8 months ago

Can you try to add -u attribute:

await foreach (var cmdEvent in Cli.Wrap("python").WithArguments("-u test.py Joe").ListenAsync())
{
    switch (cmdEvent)
    {
        case StartedCommandEvent started:
            Console.WriteLine($"Process started; ID: {started.ProcessId}");
            break;
        case StandardOutputCommandEvent stdOut:
            Console.WriteLine($"Out> {stdOut.Text}");
            break;
        case StandardErrorCommandEvent stdErr:
            Console.WriteLine($"Err> {stdErr.Text}");
            break;
        case ExitedCommandEvent exited:
            Console.WriteLine($"Process exited; Code: {exited.ExitCode}");
            break;
    }
}

Or set environment before: PYTHONUNBUFFERED=x

More info: -u forces the stdout and stderr streams to be unbuffered; this option has no effect on stdin; also PYTHONUNBUFFERED=x