I am trying to run "ipconfig" cmd on remote machine. In cmd prompt, I am getting the complete output. But through C# code, I am getting only first line of output.
public void RunCmd()
{
string command = "cmd.exe";
string arguments = #"/c C:\PSTools\PsExec.exe -accepteula -i \\xxxx -s -u xxxxx -p xxxx cmd /c ipconfig";
string workingDirectory = #"C:\PSTools\";
ProcessStartInfo procStartInfo = new ProcessStartInfo();
procStartInfo.FileName = command;
procStartInfo.RedirectStandardOutput = true;
procStartInfo.RedirectStandardError = true;
procStartInfo.RedirectStandardInput = true;
procStartInfo.UseShellExecute = false;
procStartInfo.CreateNoWindow = true;
if (arguments != null)
procStartInfo.Arguments = arguments;
if (workingDirectory != null)
procStartInfo.WorkingDirectory = workingDirectory;
Process process = process = new Process();
process.StartInfo = procStartInfo;
process.EnableRaisingEvents = true;
process.OutputDataReceived += new DataReceivedEventHandler(
(s, e) =>
{
Console.WriteLine($"{e.Data}");
}
);
process.ErrorDataReceived += new DataReceivedEventHandler((s, e) =>
{
Console.WriteLine($"{e.Data}");
});
process.Start();
//process.StandardInput.AutoFlush = true;
process.BeginOutputReadLine();
process.BeginErrorReadLine();
process.WaitForExit();
}
I would like to run some docker commands programmatically inside a process, and capture the standard output of docker. The commands do run without problems, but I cannot capture the standard output. For example, consider the following code. I would like to the read the docker info command, but all I get is a null string. Do you if there is a way to interact with docker in this way? Thanks
private static void VerifyDocker()
{
var processStartInfo = new ProcessStartInfo();
var messagesBuilder = new StringBuilder();
var arguments = "info --format '{{json .}}'";
using (var process = new Process())
{
processStartInfo.FileName = "docker";
processStartInfo.Arguments = arguments;
processStartInfo.CreateNoWindow = true;
processStartInfo.UseShellExecute = false;
processStartInfo.RedirectStandardOutput = true;
process.OutputDataReceived += (sender, e) => messagesBuilder.Append(e.Data);
process.StartInfo = processStartInfo;
process.Start();
process.BeginOutputReadLine();
process.WaitForExit();
process.CancelOutputRead();
var message = messagesBuilder.ToString();
}
}
Yes it's a good idea to capture both, and also use double quotes instead of single quotes for the parameter passed to --format.
private static void VerifyDocker()
{
var processStartInfo = new ProcessStartInfo();
var messagesBuilder = new StringBuilder();
var errorMessagesBuilder = new StringBuilder();
var arguments = "info --format \"{{json .}}\"";
using (var process = new Process())
{
processStartInfo.FileName = "docker";
processStartInfo.Arguments = arguments;
processStartInfo.CreateNoWindow = true;
processStartInfo.UseShellExecute = false;
processStartInfo.RedirectStandardOutput = true;
processStartInfo.RedirectStandardError = true;
process.OutputDataReceived += (sender, e) => messagesBuilder.Append(e.Data);
process.ErrorDataReceived += (sender, e) => errorMessagesBuilder.Append(e.Data);
process.StartInfo = processStartInfo;
process.Start();
process.BeginOutputReadLine();
process.BeginErrorReadLine();
process.WaitForExit();
process.CancelOutputRead();
process.CancelErrorRead();
var message = messagesBuilder.ToString();
var errrors = errorMessagesBuilder.ToString();
}
}
I'm trying to use "multi-step" command in a c# script, for example the command "net user usrname *" contains 3 steps to enter a password and then validate, i don't know if it is possible to send extra arguments while the Process is running
My code:
Process p = new Process();
p.StartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
p.StartInfo.CreateNoWindow = true;
p.StartInfo.FileName = "cmd.exe";
p.StartInfo.Arguments = "/C " + command;
p.StartInfo.WorkingDirectory = startupFolder;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.RedirectStandardError = true;
p.StartInfo.UseShellExecute = false;
p.Start();
string output = p.StandardOutput.ReadToEnd();
string error = p.StandardError.ReadToEnd();
You would concatenate each command with "&". For example, "cmd /k echo Test 1 & echo test 2".
Edit:
I created a remote control/remote admin solution a while back that uses this same technique to allow you to run batch and PowerShell scripts against remote computers via the web portal. As shown in the below screenshot, it works.
The C# that executes the command can be found here: https://github.com/Jay-Rad/InstaTech_Client/blob/master/InstaTech_Service/Socket.cs#L614
if (cmdProcess == null || cmdProcess.HasExited)
{
var psi2 = new ProcessStartInfo("cmd.exe", "/k " + command);
psi2.RedirectStandardOutput = true;
psi2.RedirectStandardInput = true;
psi2.RedirectStandardError = true;
psi2.UseShellExecute = false;
psi2.WorkingDirectory = Path.GetPathRoot(Environment.SystemDirectory);
cmdProcess = new Process();
cmdProcess.StartInfo = psi2;
cmdProcess.EnableRaisingEvents = true;
cmdProcess.OutputDataReceived += async (object sender, DataReceivedEventArgs args) =>
{
jsonMessage.Status = "ok";
jsonMessage.Output = args.Data;
await SocketSend(jsonMessage);
};
cmdProcess.ErrorDataReceived += async (object sender, DataReceivedEventArgs args) =>
{
jsonMessage.Status = "ok";
jsonMessage.Output = args.Data;
await SocketSend(jsonMessage);
};
cmdProcess.Start();
cmdProcess.BeginOutputReadLine();
cmdProcess.BeginErrorReadLine();
}
else
{
cmdProcess.StandardInput.WriteLine(command);
}
I have create a C# application for make easy of committing changes to mercurial repository. i have use following code to run hg commands in c# process
static void ExecuteCMDCommand(string path, string command)
{
ProcessStartInfo startInfo = new ProcessStartInfo();
//startInfo.CreateNoWindow = false;
startInfo.WorkingDirectory = path;
startInfo.UseShellExecute = false;
startInfo.FileName = "hg.exe";
startInfo.RedirectStandardOutput = true;
startInfo.RedirectStandardInput = true;
startInfo.RedirectStandardError = true;
//startInfo.WindowStyle = ProcessWindowStyle.Hidden;
startInfo.Arguments = command;
Process myProcess = new Process();
try
{
myProcess.StartInfo = startInfo;
var sbOut = new StringBuilder();
var sbErr = new StringBuilder();
var sw = new Stopwatch();
sw.Start();
myProcess.Start();
TimeSpan firstRead = TimeSpan.Zero;
myProcess.OutputDataReceived += (s, e) =>
{
if (firstRead == TimeSpan.Zero)
{
firstRead = sw.Elapsed;
}
sbOut.Append(e.Data);
};
Console.WriteLine("**************" + sbOut.ToString());
myProcess.ErrorDataReceived += (s, e) => sbErr.Append(e.Data);
myProcess.BeginOutputReadLine();
myProcess.BeginErrorReadLine();
var eventsStarted = sw.Elapsed;
myProcess.WaitForExit();
var processExited = sw.Elapsed;
sw.Reset();
// string strOutput = myProcess.StandardOutput.ReadToEnd();
}
catch (Exception ex)
{
throw ex;
}
finally {
myProcess.Close();
myProcess.Dispose();
}
}
this works for hg status/hg branches but when i execute hg pull it is not working, i only see pulling from http://abc.abc.com/hg/repo.
What should i do for this?
I'm trying to execute multiple commands without create a new process each time. Basically, I want to start the DOS command shell, switch to the MySQL command shell, and execute a command. Here's how I am calling the procedure (also below). Also, how do I handle the "\"'s in the command?
ExecuteCommand("mysql --user=root --password=sa casemanager", 100, false);
ExecuteCommand(#"\. " + Environment.CurrentDirectory + #"\MySQL\CaseManager.sql", 100, true);
private void ExecuteCommand(string Command, int Timeout, Boolean closeProcess)
{
ProcessStartInfo ProcessInfo;
Process Process;
ProcessInfo = new ProcessStartInfo("cmd.exe", "/C " + Command);
ProcessInfo.CreateNoWindow = false;
ProcessInfo.UseShellExecute = false;
Process = Process.Start(ProcessInfo);
Process.WaitForExit(Timeout);
if (closeProcess == true) { Process.Close(); }
}
You can redirect standard input and use a StreamWriter to write to it:
Process p = new Process();
ProcessStartInfo info = new ProcessStartInfo();
info.FileName = "cmd.exe";
info.RedirectStandardInput = true;
info.UseShellExecute = false;
p.StartInfo = info;
p.Start();
using (StreamWriter sw = p.StandardInput)
{
if (sw.BaseStream.CanWrite)
{
sw.WriteLine("mysql -u root -p");
sw.WriteLine("mypassword");
sw.WriteLine("use mydb;");
}
}
const string strCmdText = "/C command1&command2";
Process.Start("CMD.exe", strCmdText);
Couldn't you just write all the commands into a .cmd file in the temp folder and then execute that file?
As another answer alludes to under newer versions of Windows it seems to be necessary to read the standard output and/or standard error streams otherwise it will stall between commands. A neater way to do that instead of using delays is to use an async callback to consume output from the stream:
static void RunCommands(List<string> cmds, string workingDirectory = "")
{
var process = new Process();
var psi = new ProcessStartInfo();
psi.FileName = "cmd.exe";
psi.RedirectStandardInput = true;
psi.RedirectStandardOutput = true;
psi.RedirectStandardError = true;
psi.UseShellExecute = false;
psi.WorkingDirectory = workingDirectory;
process.StartInfo = psi;
process.Start();
process.OutputDataReceived += (sender, e) => { Console.WriteLine(e.Data); };
process.ErrorDataReceived += (sender, e) => { Console.WriteLine(e.Data); };
process.BeginOutputReadLine();
process.BeginErrorReadLine();
using (StreamWriter sw = process.StandardInput)
{
foreach (var cmd in cmds)
{
sw.WriteLine (cmd);
}
}
process.WaitForExit();
}
I prefer to do it by using a BAT file.
With BAT file you have more control and can do whatever you want.
string batFileName = path + #"\" + Guid.NewGuid() + ".bat";
using (StreamWriter batFile = new StreamWriter(batFileName))
{
batFile.WriteLine($"YOUR COMMAND");
batFile.WriteLine($"YOUR COMMAND");
batFile.WriteLine($"YOUR COMMAND");
}
ProcessStartInfo processStartInfo = new ProcessStartInfo("cmd.exe", "/c " + batFileName);
processStartInfo.UseShellExecute = true;
processStartInfo.CreateNoWindow = true;
processStartInfo.WindowStyle = ProcessWindowStyle.Normal;
Process p = new Process();
p.StartInfo = processStartInfo;
p.Start();
p.WaitForExit();
File.Delete(batFileName);
ProcessStartInfo pStartInfo = new ProcessStartInfo();
pStartInfo.FileName = "CMD";
pStartInfo.Arguments = #"/C mysql --user=root --password=sa casemanager && \. " + Environment.CurrentDirectory + #"\MySQL\CaseManager.sql"
pStartInfo.WindowStyle = ProcessWindowStyle.Hidden;
Process.Start(pStartInfo);
The && is the way to tell the command shell that there is another command to execute.
A command-line process such cmd.exe or mysql.exe will usually read (and execute) whatever you (the user) type in (at the keyboard).
To mimic that, I think you want to use the RedirectStandardInput property: http://msdn.microsoft.com/en-us/library/system.diagnostics.processstartinfo.redirectstandardinput.aspx
You could also tell MySQL to execute the commands in the given file, like so:
mysql --user=root --password=sa casemanager < CaseManager.sql
You need to READ ALL data from input, before send another command!
And you can't ask to READ if no data is avaliable... little bit suck isn't?
My solutions... when ask to read... ask to read a big buffer... like 1 MEGA...
And you will need wait a min 100 milliseconds... sample code...
Public Class Form1
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim oProcess As New Process()
Dim oStartInfo As New ProcessStartInfo("cmd.exe", "")
oStartInfo.UseShellExecute = False
oStartInfo.RedirectStandardOutput = True
oStartInfo.RedirectStandardInput = True
oStartInfo.CreateNoWindow = True
oProcess.StartInfo = oStartInfo
oProcess.Start()
Dim Response As String = String.Empty
Dim BuffSize As Integer = 1024 * 1024
Dim x As Char() = New Char(BuffSize - 1) {}
Dim bytesRead As Integer = 0
oProcess.StandardInput.WriteLine("dir")
Threading.Thread.Sleep(100)
bytesRead = oProcess.StandardOutput.Read(x, 0, BuffSize)
Response = String.Concat(Response, String.Join("", x).Substring(0, bytesRead))
MsgBox(Response)
Response = String.Empty
oProcess.StandardInput.WriteLine("dir c:\")
Threading.Thread.Sleep(100)
bytesRead = 0
bytesRead = oProcess.StandardOutput.Read(x, 0, BuffSize)
Response = String.Concat(Response, String.Join("", x).Substring(0, bytesRead))
MsgBox(Response)
End Sub
End Class
I'm using these methods:
public static Process StartCommand(params string[] commands) => StartCommand(commands, false);
public static Process StartCommand(IEnumerable<string> commands, bool inBackground, bool runAsAdministrator = true)
{
Process p = new Process();
p.StartInfo.FileName = "cmd.exe";
if(commands.Any()) p.StartInfo.Arguments = #"/C " + string.Join("&&", commands);
if (runAsAdministrator)
p.StartInfo.Verb = "runas";
if (inBackground)
{
p.StartInfo.CreateNoWindow = true;
p.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
}
p.Start();
return p;
}
Enjoy...