I'm trying to write a C# program that captures the standard output in a python program. My problem is that all of the output comes after the program has executed rather than when it actually happens. As an example, for this python program:
print "Hello"
time.sleep(2)
print "Hello"
I would expect to get "Hello", a two second gap, and then another "Hello". The actual result is a two second gap and then "Hello", "Hello".
If I run the above python script from the command line, I get the desired behaviour. If the command prompt can do this, then I should be able to mimic that functionality without having to flush the buffer repeatedly.
I'm using this to run the process from C#:
_proc = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = "C:\\Python27\\python.exe",
Arguments = pyScript,
RedirectStandardError = true,
UseShellExecute = false,
RedirectStandardOutput = true,
CreateNoWindow = true
}
};
_proc.OutputDataReceived += ProcOnOutputDataReceived;
_proc.Start();
_proc.BeginOutputReadLine();
I can run this C# code (and changing the ProcessStartInfo properties above to run C# executable) and it behaves correctly:
Console.WriteLine("Hello");
Thread.Sleep(2000);
Console.WriteLine("Hello");
With this code I get "Hello", a two second gap, and then another "Hello".
Any idea why? How can I get the python interpreter to send the standard output as it happens?
I know this is old but running python with -u option (unbuffered) seems to be what you were after.
Unbuffered will not wait before flushing output.
Running python without this option, output is flushed when:
The buffer is full
You call sys.stdout.flush()
The process ends
You need to flush the output buffer.
import sys
sys.stdout.flush()
See this question: How to flush output of Python print?
Good
(Capture standard output of a python program as it happens)
Em console is NEED:
import sys
sys.stdout.flush()
Related
I was trying to start a exe with arguments by Process.Start.
My first try is using Process.Start("Path/of/the/exe", "arguments of exe").
Here's my code snippets:
Process.Start(#"D:\Program Files\ITASCA\UDEC700\Exe64\udecConsole2017.exe", #"call 'D:\Work\202205\20220525\tunnel-for-cmd.txt'");
However the initialization of this exe is a bit slow, and the result is, I can only start the exe but the failed passing arguments. The following is the screenshot:
which is exactly the same result that starts without arguments.
By referencing this post C# - Making a Process.Start wait until the process has start-up, I changed my code as follows:
var process = Process.Start(#"D:\Program Files\ITASCA\UDEC700\Exe64\udecConsole2017.exe", #"call 'D:\Work\202205\20220525\tunnel-for-cmd.txt'");
while (string.IsNullOrEmpty(process.MainWindowTitle))
{
System.Threading.Thread.Sleep(100);
process.Refresh();
}
however these changes does not work.
I think my goal is to wait until exe completely started and then run it with arguments, but I dont know how to implement this.
=====================================================
New additions:
if I type in arguments call 'D:\Work\202205\20220525\tunnel-for-cmd.txt' in this started process, I will get my result:
SO I think the input arguments should be OK?
=======================================
new addition 2:
code for checking outputstream end
It appears this is a console application and you are typing in the console after it starts. This typing is not arguments: Arguments are provided only when starting a new process and never change.
What you are doing is providing something to the standard input of the program. Console programs have three streams the OS provides (one input and two output). You need to redirect these to detect when the program has started and to provide the proper input.
Something like this:
// Start with stdio redirected
var psi = new ProcessStartInfo()
{
UseShellExecute = false,
FileName = #"your exe",
RedirectStandardInput = true,
RedirectStandardOutput = true,
};
var p = System.Diagnostics.Process.Start(psi);
// Read until the udec> prompt
while(true)
{
var line = p.StandardOutput.ReadLine();
if(line.StartsWith("udec>"))
break;
}
// Write the command
p.StandardInput.WriteLine(#"call 'D:\Work\202205\20220525\tunnel-for-cmd.txt'");
// Read the result
p.StandardOutput.ReadToEnd();
I'm trying to write a console application that starts wsl in a background process and streams different commands to it for some automation stuff.
Important: I cannot just do wsl -- mycommand and then exit as I need wsl to stay running in the background thus I chose to keep the process alive as long as my application runs.
To start the process I'm using Process.Start
var startInfo = new ProcessStartInfo
{
FileName = "wsl",
CreateNoWindow = true,
RedirectStandardInput = true,
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false
};
var process = Process.Start(startInfo);
To execute commands I just feed them into process.StandardInput followed by \n
process.StandardInput.Write("uname -a\n");
process.StandardInput.Flush();
After that I'm able to read the commands output using e.g. process.StandardOutput.ReadLine.
The problem with the code is that I'm not able to detect when the command finished i.e. there will be no more output to capture.
uname -a outputs a single line so that case is easy to handle but what if the command writes multiple lines to stdout?
Is there a way to solve this or is my approach to do this already wrong to start with?
(I need a synchronous way to get the output after executing the command as I need to evaluate it after execution)
I have made a python console script, and converted it to a .exe with py2exe (I have used the console = ['test.py'] line in my setup file).
The program parses a file, and during the parse it prints out how much of the file it has parsed. Typical output would be:
Processing (currently at 1%)
Processing (currently at 4%)
etc.
When running the file in a cmd window it works just as expected.
I have also created a very small C# WPF program that just runs the parser:
Process p = new Process();
p.StartInfo = new ProcessStartInfo(#"C:\temp\test.exe");
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.UseShellExecute = false;
p.StartInfo.CreateNoWindow = true;
p.OutputDataReceived += new DataReceivedEventHandler(p_OutputDataReceived);
Task t = new Task(() =>
{
p.Start();
p.BeginOutputReadLine();
p.WaitForExit();
p.Close();
});
t.Start();
The p_OutputDataReceived handler just sends the received output to a textbox. This works and I have tested it on other programs, and there I get the output from the program when I expect.
However when I run my parser (the one created with py2exe) I get all outputs just after the parser has finished. So in the end I get all the correct output, but I get them all at the same time...
(note, I don't get one big output, but rather all the expected outputs but still, all at the same time)
So to be perfectly clear here:
If I run the parser from the command window, I get the outputs one by one
I have tested to run a C# console program instead of the py2exe generated program and that works (I get the outputs one by one)
Python checks if sys.stdout (the program's standard output) is a console. If it is, Python flushes write buffers immediately so the user can see it. Otherwise writes get cached and are outputted all at once:
when the write buffer is full or
at program exit.
The logic behind this is better performance, since when redirecting stdout to a file or to other programs, typically no one cares when the output occurs.
You can fix this by including sys.stdout.flush() in strategic locations in your Python parser script (i.e. directly after printing status line).
Btw: You should be able to observe the same time-delay behavior if you redirect the output of your parser to more, for example:
C:\temp\test.exe | more
I'm trying to launch ffmpeg as a Process in .NET (C#), but in some cases, depending on the arguments (specifically, if I omit video arguments in order to create an audio file), it's stalling. It launches, outputs some lines, but then just stalls (using 0% CPU). When the parent .NET process is killed, it continues, and if I let it continue, ffmpeg produces the file correctly. I thought it might be due to using Peek() to look at the stream, so I just simplified it to the following, which behaves the same:
_process = new Process
{
StartInfo =
{
UseShellExecute = false,
RedirectStandardOutput = false,
RedirectStandardError = true,
FileName = "c:\\ffmpeg.exe",
Arguments = string.Format(
"-i {0} {1} {2} {3} -y {4}", inputPath, videoArgs, audioArgs, options, outputPath)
}
};
_process.Start();
_process.WaitForExit();
ffmpeg gets to the point where it outputs information about the input video/audio streams before stalling. Executing the command via the command prompt works as expected.
Does anyone know what the problem could be?
Edit:
Just to add, I tried UseShellExecute = true (and RedirectStandardError = false), and this works. I still need to read the output, however, so this doesn't really help me.
Have a read of this
MSDN on RedirectStandardError
Apparently this is a bit fiddly and can deadlock if the output or error stream buffers get filled up. Sit's there waiting for you to read what it's wrote...
According to the comments on this answer, in some cases FFmpeg manipulates the screen memory directly without using the standard output and error streams. One example of this is the [file] already exists. Overwrite? [y/N] prompt.
I'm guessing you've run the program outside if .NET to see what the output is? If it is a prompt like the one above then it might pay to avoid the trouble and attempt to provide an argument that will skip it eg. the -y command that forces FFmpeg to overwrite a file.
I am writing a test script for a C/S system in python. And right now, we only have a test client written in C#. It is a command line based client. I want to control the input and get the output of the client in my python script. How could I make it?
Thanks!
Use the subprocess module as follows:
import subprocess
proc = subprocess.Popen("cat", stdin = subprocess.PIPE, stdout = subprocess.PIPE)
out, err = proc.communicate(input)
The process ends after the last call. If you want to do some input iteratively, you can use proc.stdin.write, but you have to call communicate(None) at the end to finish the process.
Use subprocess.Popen.