Execute multiple command lines with the same process using .NET

Viewed 116465

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

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

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

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

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

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

Related