I'm trying to process log file output and put it on the plot. However, I can't put my hands around Get-Content -Wait.
It seems that my C# program is not being invoked at all. Works fine without the wait switch, but that's not what I need.
Simple sample:
using static System.Console;
public class Program
{
public static void Main(string[] args)
{
WriteLine("Starting...");
if (IsInputRedirected)
{
while (In.Peek() != -1)
{
Write("[");
var input = In.ReadLine();
WriteLine($"{input}]");
}
WriteLine("... done.");
}
else
{
WriteLine("Nothing");
}
}
}
With the sample calls like:
gc .\Program.cs | .\bin\Debug.ConsoleTest.exe
and
gc .\Program.cs -Wait | .\bin\Debug.ConsoleTest.exe
Does anybody know how to receive the output of Get-Content with -Wait from console application?
Get-content is used to get the content of a file not strip the output of the program u start.
you can use the get-content on the logfile u genarate but know that if u use wait it will be waiting until u kill the procces waiting.
so in a nutshell Get-content -Wait is only used to follow the a log file being written from a other process.
just use .\Program.cs | .\bin\Debug.ConsoleTest.exe and it will wait until exited.
if you want the stdout and the stderror you need a construction like described Here (please note there is a problem using $stdout = $p.StandardOutput.ReadToEnd() ;$stderr = $p.StandardError.ReadToEnd() for big outputs, they deadlock each other)
Related
I am trying to create a C# console application that repeatedly runs a .bat file and saves the output into a variable to be modified later. The script is meant to get open TCP connections on a connected device using adb.exe.
I want the application to quit when the Esc key is pressed (once). To accomplish this I followed this answer and implemented it like so:
Program.cs
static void Main(string[] args)
{
Console.WriteLine("Press escape to quit");
do
{
while (!Console.KeyAvailable)
{
// Console.WriteLine("Application is running");
RunBatch();
}
} while (Console.ReadKey(true).Key != ConsoleKey.Escape);
Console.WriteLine("While loop was exited");
Console.ReadLine();
}
static void RunBatch()
{
Process p = new Process();
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.FileName = #"C:\Dev\Batch\GetTcpConnections.bat";
p.Start();
string output = p.StandardOutput.ReadToEnd();
p.WaitForExit();
Console.WriteLine(output);
}
GetTcpConnections.bat
#echo off
echo %time%
adb.exe shell cat /proc/net/tcp
timeout /t 1 /nobreak>nul
The expected result would be, that the console application exits the loop and prints While loop was exited directly upon pressing Esc. It works like this when I comment RunBatch() and un-comment Console.WriteLine("Application is running". However, when I am running the batch script, I need to hold down Esc for about a second or two before the program exits the while loop, instead of being instantaneous.
At first I thought that the input may be blocked by timeout /t 1 /nobreak>nul in the batch script, but removing this line made no difference. Am I missing something else here that could block inputs?
As soon as your consoleapp starts adb.exe, it looses the focus. When an applicaties does not have the focus, it does not receive any keyboard input, because the keyboard input goes to another focused application.
You can reclaim the focus by selecting the consoleapp with your mouse, while adb.exe is running, and than press ESC. But I guess that is not what you want.
I see serveral "solutions":
You could find a way to make your consoleapp always the top-level applicatie.
Make it a Desktop / Winform-application which has a bug "QUIT"-button.
The code below should solve your problem. Note that I have moved the timeout away from the batch file and placed it within the while loop.
Program.cs
private static void Main(string[] args)
{
Console.WriteLine("Press escape to quit");
do
{
while (!Console.KeyAvailable)
{
RunBatch();
Thread.Sleep(1000);
}
} while (Console.ReadKey(true).Key != ConsoleKey.Escape);
Console.WriteLine("While loop has exited");
Console.ReadLine();
}
private static void RunBatch()
{
var process = new Process
{
StartInfo =
{
UseShellExecute = false,
RedirectStandardOutput = true,
FileName = #"C:\Dev\Batch\GetTcpConnections.bat"
}
};
process.Start();
Console.WriteLine(process.StandardOutput.ReadToEnd());
}
GetTcpConnections.bat
#echo off
echo %time%
adb.exe shell cat /proc/net/tcp
Have C# Console application which read/writes on standard input and output.
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Console application");
while(true)
{
int input = Console.Read();
Console.WriteLine(input.ToString());
}
}
}
I have other native application which launch above console application and read/writes with help of pipe communication.
C# console application writes are read successfully in external application and also initial writes from external application works fine and then it loops to read from the C# console application.
After that writes from external application on the pipe doesn't work.
Absurd thing is problem is happening on some of the operating system and few it is working fine.
C# Console application compile with .NET Framework 4 / Client Profile.
Known issue from MS :
http://support.microsoft.com/en-us/kb/2675468
Please check the KB for more details along with sample provided.
Try
static void Main(string[] args)
{
Console.SetIn(new StreamReader(Console.OpenStandardInput()));
while (Console.In.Peek() != -1)
{
string input = Console.In.ReadLine();
Console.WriteLine(input);
}
}
Does it work that way?
EDIT: I have updated my answer.
Sample usage/output:
dir /B | ConsoleApplication1.exe
produces:
ConsoleApplication1.exe
ConsoleApplication1.exe.config
ConsoleApplication1.pdb
ConsoleApplication1.vshost.exe
ConsoleApplication1.vshost.exe.config
ConsoleApplication1.vshost.exe.manifest
When I try your original code, it ends up spamming -1 constantly.
You can also try:
while (true)
{
int input = Console.Read();
if (input != -1) { // -1 = no input
Console.WriteLine(input.ToString());
}
}
Now it handles every character separately.
Is there a way for a Console app know whether it has been called from a batch file as opposed to directly at the command prompt?
The reason for asking is to find a way to decide whether to initiate a Console.ReadLine loop or similar to await further input, or whether to exit immediately.
Alternatively, is there a way for a batch file to continue sending input to a Console App that is awaiting further input via ReadLine?
Yes, I know - that's 2 questions. If anyone comments that there's an answer to the second question I'll ask that separately.
Why not pass in a commandline argument to the console app to determine whether to quit immediately or wait.
The batch file can set an environment variable and you can check that in your console application:
in the batch file:
set waitOnDone=0
yourExeFile -arguments
in your Console Application:
var env = System.Environment.GetEnvironmentVariable("waitOnDone");
if (String.IsNullOrEmpty(env) || env != "0")
{
// do something
}
If the batch file knows the input then save the input to a file and feed that to your program like
prog.exe <argument.txt
in the batch file. I think you need not change the source code for this.
Possibly your problem is to read only from stdin if there is a redirecton (from your batch file).
This can also be solved (with dotnet) by detecting if there is an input stream.
Solution from #Hans Passant, SO: how to detect if console in stdin has been redirected
using System.Runtime.InteropServices;
public static class ConsoleEx
{
public static bool OutputRedirected
{
get { return FileType.Char != GetFileType(GetStdHandle(StdHandle.Stdout)); }
}
public static bool InputRedirected
{
get { return FileType.Char != GetFileType(GetStdHandle(StdHandle.Stdin)); }
}
public static bool ErrorRedirected
{
get { return FileType.Char != GetFileType(GetStdHandle(StdHandle.Stderr)); }
}
// P/Invoke:
private enum FileType { Unknown, Disk, Char, Pipe };
private enum StdHandle { Stdin = -10, Stdout = -11, Stderr = -12 };
[DllImport("kernel32.dll")]
private static extern FileType GetFileType(IntPtr hdl);
[DllImport("kernel32.dll")]
private static extern IntPtr GetStdHandle(StdHandle std);
}
And it can be used like this
if (ConsoleEx.InputRedirected)
{
string strStdin = Console.In.ReadToEnd();
}
I am writing a url shortener app and I would like to also create a console app with C# to push the URLs to a WCF service which I have also created.
WCF app will shorten the url on this URI;
http://example.com/shorten/http://exaple.com
so what I want is just that.
My console exe file will be sitting inside c:\dev folder and on Windows command line, I would like to do this;
c:\dev>myapp -throw http://example.com
with this method I would like to talk to that service. there is no problem on talking part. But the problem is how can I supply this -throw thing on the command line and get a response and put that response on the command line and supply a method to copy that to the clipboard. Am I asking too much here? :S I don't know.
Could you direct me somewhere that I can find information on that or could u please give me an example code of this?
Thanks.
EDIT :
I have tried the following code;
class Program {
static void Main(string[] args) {
if (args[0] == "-throw") {
System.Windows.Forms.Clipboard.SetDataObject(args[1]);
Console.WriteLine(args[1] + " has been added to clipboard !");
Console.ReadLine();
}
}
}
and I received the following error;
C:\Apps\ArgsTry\ArgsTry\bin\Debug>ArgsTry
-throw man
Unhandled Exception:
System.Threading.ThreadStateException:
Current thread must be set to single
thread apartment (STA) mode before OLE
calls can be made. Ensur e that your
Main function has STAThreadAttribute
marked on it. at
System.Windows.Forms.Clipboard.SetDataObject(Object
data, Boolean copy, In t32 retryTimes,
Int32 retryDelay) at
System.Windows.Forms.Clipboard.SetDataObject(Object
data) at
ArgsTry.Program.Main(String[] args) in
c:\apps\ArgsTry\ArgsTry\Program.cs:
line 14
C:\Apps\ArgsTry\ArgsTry\bin\Debug>
Passing arguments to a console application is easy:
using System;
public class CommandLine
{
public static void Main(string[] args)
{
for(int i = 0; i < args.Length; i++)
{
if( args[i] == "-throw" )
{
// call http client args[i+1] for URL
}
}
}
}
As for the clipboard, see:
http://msdn.microsoft.com/en-us/library/system.windows.forms.clipboard.aspx
See the args below, you can use it to read all the values passed when you run your exe file.
static void Main(string[] args) {
I wrote a quick and dirty wrapper around svn.exe to retrieve some content and do something with it, but for certain inputs it occasionally and reproducibly hangs and won't finish. For example, one call is to svn list:
svn list "http://myserver:84/svn/Documents/Instruments/" --xml --no-auth-cache --username myuser --password mypassword
This command line runs fine when I just do it from a command shell, but it hangs in my app. My c# code to run this is:
string cmd = "svn.exe";
string arguments = "list \"http://myserver:84/svn/Documents/Instruments/\" --xml --no-auth-cache --username myuser --password mypassword";
int ms = 5000;
ProcessStartInfo psi = new ProcessStartInfo(cmd);
psi.Arguments = arguments;
psi.RedirectStandardOutput = true;
psi.WindowStyle = ProcessWindowStyle.Normal;
psi.UseShellExecute = false;
Process proc = Process.Start(psi);
StreamReader output = new StreamReader(proc.StandardOutput.BaseStream, Encoding.UTF8);
proc.WaitForExit(ms);
if (proc.HasExited)
{
return output.ReadToEnd();
}
This takes the full 5000 ms and never finishes. Extending the time doesn't help. In a separate command prompt, it runs instantly, so I'm pretty sure it's unrelated to an insufficient waiting time. For other inputs, however, this seems to work fine.
I also tried running a separate cmd.exe here (where exe is svn.exe and args is the original arg string), but the hang still occurred:
string cmd = "cmd";
string arguments = "/S /C \"" + exe + " " + args + "\"";
What could I be screwing up here, and how can I debug this external process stuff?
EDIT:
I'm just now getting around to addressing this. Mucho thanks to Jon Skeet for his suggestion, which indeed works great. I have another question about my method of handling this, though, since I'm a multi-threaded novice. I'd like suggestions on improving any glaring deficiencies or anything otherwise dumb. I ended up creating a small class that contains the stdout stream, a StringBuilder to hold the output, and a flag to tell when it's finished. Then I used ThreadPool.QueueUserWorkItem and passed in an instance of my class:
ProcessBufferHandler bufferHandler = new ProcessBufferHandler(proc.StandardOutput.BaseStream,
Encoding.UTF8);
ThreadPool.QueueUserWorkItem(ProcessStream, bufferHandler);
proc.WaitForExit(ms);
if (proc.HasExited)
{
bufferHandler.Stop();
return bufferHandler.ReadToEnd();
}
... and ...
private class ProcessBufferHandler
{
public Stream stream;
public StringBuilder sb;
public Encoding encoding;
public State state;
public enum State
{
Running,
Stopped
}
public ProcessBufferHandler(Stream stream, Encoding encoding)
{
this.stream = stream;
this.sb = new StringBuilder();
this.encoding = encoding;
state = State.Running;
}
public void ProcessBuffer()
{
sb.Append(new StreamReader(stream, encoding).ReadToEnd());
}
public string ReadToEnd()
{
return sb.ToString();
}
public void Stop()
{
state = State.Stopped;
}
}
This seems to work, but I'm doubtful that this is the best way. Is this reasonable? And what can I do to improve it?
One standard issue: the process could be waiting for you to read its output. Create a separate thread to read from its standard output while you're waiting for it to exit. It's a bit of a pain, but that may well be the problem.
Jon Skeet is right on the money!
If you don't mind polling after you launch your svn command try this:
Process command = new Process();
command.EnableRaisingEvents = false;
command.StartInfo.FileName = "svn.exe";
command.StartInfo.Arguments = "your svn arguments here";
command.StartInfo.UseShellExecute = false;
command.StartInfo.RedirectStandardOutput = true;
command.Start();
while (!command.StandardOutput.EndOfStream)
{
Console.WriteLine(command.StandardOutput.ReadLine());
}
I had to drop an exe on a client's machine and use Process.Start to launch it.
The calling application would hang - the issue ended up being their machine assuming the exe was dangerous and preventing other applications from starting it.
Right click the exe and go to properties. Hit "Unblock" toward the bottom next to the security warning.
Based on Jon Skeet's answer this is how I do it in modern day (2021) .NET 5
var process = Process.Start(processStartInfo);
var stdErr = process.StandardError;
var stdOut = process.StandardOutput;
var resultAwaiter = stdOut.ReadToEndAsync();
var errResultAwaiter = stdErr.ReadToEndAsync();
await process.WaitForExitAsync();
await Task.WhenAll(resultAwaiter, errResultAwaiter);
var result = resultAwaiter.Result;
var errResult = errResultAwaiter.Result;
Note that you can't await the standard output before the error, because the wait will hang in case the standard error buffer gets full first (same for trying it the other way around).
The only way is to start reading them asynchronously, wait for the process to exit, and then complete the await by using Task.WaitAll
I know this is an old post but maybe this will assist someone. I used this to execute some AWS (Amazon Web Services) CLI commands using .Net TPL tasks.
I did something like this in my command execution which is executed within a .Net TPL Task which is created within my WinForm background worker bgwRun_DoWork method which holding a loop with while(!bgwRun.CancellationPending). This contains the reading of the Standard Output from the Process via a new Thread using the .Net ThreadPool class.
private void bgwRun_DoWork(object sender, DoWorkEventArgs e)
{
while (!bgwRun.CancellationPending)
{
//build TPL Tasks
var tasks = new List<Task>();
//work to add tasks here
tasks.Add(new Task(()=>{
//build .Net ProcessInfo, Process and start Process here
ThreadPool.QueueUserWorkItem(state =>
{
while (!process.StandardOutput.EndOfStream)
{
var output = process.StandardOutput.ReadLine();
if (!string.IsNullOrEmpty(output))
{
bgwRun_ProgressChanged(this, new ProgressChangedEventArgs(0, new ExecutionInfo
{
Type = "ExecutionInfo",
Text = output,
Configuration = s3SyncConfiguration
}));
}
if (cancellationToken.GetValueOrDefault().IsCancellationRequested)
{
break;
}
}
});
});//work Task
//loop through and start tasks here and handle completed tasks
} //end while
}
I know my SVN repos can run slow sometimes, so maybe 5 seconds isn't long enough? Have you copied the string you are passing to the process from a break point so you are positive it's not prompting you for anything?