71

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(); }
}

10 Answers 10

87

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;");
            }
        }
Sign up to request clarification or add additional context in comments.

4 Comments

i have tried this and the process seems to exit before commands can be piped to it. could somebody point me to where i'm going wrong or has the usage changed in the past few years?
Works fine for me. Perhaps this can be marked as answer?
This didn't work for me in Corporate environment; I presume because there's some security preventing RedirectStandardInput. Calling CMD with && concatenating (see below) worked though.
In my case I had to add sw.WriteLine("exit"); to make the process quit
64
const string strCmdText = "/C command1&command2";
Process.Start("CMD.exe", strCmdText);

2 Comments

This is what I was looking for. Ot also works with speci commands like IF.
This one also works const string strCmdText = "/C (command1)&(command2)";
15

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();
}

Comments

14

Couldn't you just write all the commands into a .cmd file in the temp folder and then execute that file?

3 Comments

Not sure why this is being flagged as not an answer, just because it ends with a question mark doesn't make it not an answer =p
I was wondernig about a similar solution, but in some cases (like mine) there might be passwords included, so you should hope there is no crash in the wrong moment and erase the file properly.. so it would have drawbacks as well.
Also people get confused if you distribute your program with too many batch files.
7

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);

Comments

6
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.

1 Comment

&& means if the left command fails, the right one won't get executed, see support.microsoft.com/kb/279253/en-us. You can just use the single & in case that's not desired
3

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

1 Comment

Very useful, e. g. if the commands might contain an ampersand. (I will have user-entered text in them, so they might contain anything, I assume I would get my ampersand misinterpreted as "new command" with above solution.)
1

You could also tell MySQL to execute the commands in the given file, like so:

mysql --user=root --password=sa casemanager < CaseManager.sql

Comments

0

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

Comments

0

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...

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.