69

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(); }
}
John Saunders
  • 160,644
  • 26
  • 247
  • 397

10 Answers10

83

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;");
            }
        }
DotNetUser
  • 6,494
  • 1
  • 25
  • 27
scottm
  • 27,829
  • 22
  • 107
  • 159
  • 1
    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? – calben Jul 13 '15 at 18:25
  • 4
    Works fine for me. Perhaps this can be marked as answer? – Josh Sep 02 '15 at 17:11
  • 1
    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. – 9swampy Feb 27 '18 at 11:45
62
const string strCmdText = "/C command1&command2";
Process.Start("CMD.exe", strCmdText);
Filip
  • 2,285
  • 1
  • 19
  • 24
14

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

M4N
  • 94,805
  • 45
  • 217
  • 260
  • 10
    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 – Ja͢ck Dec 13 '13 at 03:40
  • 1
    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. – Andreas Reiff Feb 15 '14 at 17:38
  • Also people get confused if you distribute your program with too many batch files. – Tomáš Zato Jan 04 '16 at 12:27
14

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();
}
PeterJ
  • 3,705
  • 28
  • 51
  • 71
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);
Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Hermes Monteiro
  • 101
  • 1
  • 3
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.

Michael Petrotta
  • 59,888
  • 27
  • 145
  • 179
Jimmy
  • 61
  • 1
  • 1
  • 7
    `&&` means if the left command fails, the right one won't get executed, see http://support.microsoft.com/kb/279253/en-us. You can just use the single `&` in case that's not desired – Filip Apr 12 '13 at 14:17
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

ChrisW
  • 54,973
  • 13
  • 116
  • 224
  • 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.) – Andreas Reiff Feb 15 '14 at 17:12
1

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

mysql --user=root --password=sa casemanager < CaseManager.sql
Ben Hoffstein
  • 102,129
  • 8
  • 104
  • 120
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
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...

MiMFa
  • 981
  • 11
  • 14