Instrukcje: wykonywanie wiersza polecenia w języku C #, uzyskiwanie wyników STD OUT


472

Jak wykonać program wiersza polecenia z C # i odzyskać wyniki STD OUT? W szczególności chcę uruchomić DIFF na dwóch programowo wybranych plikach i zapisać wyniki w polu tekstowym.


2
Zobacz także stackoverflow.com/a/5367686/492 - pokazuje zdarzenia dla danych wyjściowych i błędów.
CAD bloke

Powiązane (ale bez przechwytywania STDOUT): stackoverflow.com/questions/1469764
user202729

Odpowiedzi:


523
// Start the child process.
 Process p = new Process();
 // Redirect the output stream of the child process.
 p.StartInfo.UseShellExecute = false;
 p.StartInfo.RedirectStandardOutput = true;
 p.StartInfo.FileName = "YOURBATCHFILE.bat";
 p.Start();
 // Do not wait for the child process to exit before
 // reading to the end of its redirected stream.
 // p.WaitForExit();
 // Read the output stream first and then wait.
 string output = p.StandardOutput.ReadToEnd();
 p.WaitForExit();

Kod pochodzi z MSDN .


8
Czy można to zrobić bez pliku wsadowego? Rzecz w tym, że muszę wysłać parametry do polecenia. Korzystam z xsd.exe <Assembly> / type: <ClassName>, więc muszę mieć możliwość ustawienia zarówno zestawu, jak i nazwy klasy, a następnie uruchomić polecenie.
Carlo,

26
Możesz dodać argumenty do swojego wywołania poprzez {YourProcessObject}.StartInfo.Argumentsciąg.
patridge

5
Jak uruchomić proces jako administrator?
Saher Ahwal

5
Napotkałem szereg problemów, w których mój proces, używając tego kodu, całkowicie się zatrzymuje, ponieważ proces zapisał wystarczającą ilość danych do p.StandardErrorstrumienia. Kiedy strumień zapełni, wydaje się, że proces zostanie wstrzymany, dopóki dane są zużyte, więc muszę czytać zarówno StandardErrororaz StandardOutputw celu zagwarantowania, że to zadanie wykonuje poprawnie.
Ted Spence

5
Szybki headsup z kompilatora c #: Obiekt Process musi mieć właściwość UseShellExecute ustawioną na false, aby przekierowywać strumienie We / Wy.
IbrarMumtaz

144

Oto krótka próbka:

//Create process
System.Diagnostics.Process pProcess = new System.Diagnostics.Process();

//strCommand is path and file name of command to run
pProcess.StartInfo.FileName = strCommand;

//strCommandParameters are parameters to pass to program
pProcess.StartInfo.Arguments = strCommandParameters;

pProcess.StartInfo.UseShellExecute = false;

//Set output of program to be written to process output stream
pProcess.StartInfo.RedirectStandardOutput = true;   

//Optional
pProcess.StartInfo.WorkingDirectory = strWorkingDirectory;

//Start the process
pProcess.Start();

//Get program output
string strOutput = pProcess.StandardOutput.ReadToEnd();

//Wait for process to finish
pProcess.WaitForExit();

2
+1 za pokazanie, jak dodawać argumenty do działania programu wiersza polecenia (którego nie ma zaakceptowana odpowiedź)
Suman

104

Przydał mi się jeszcze jeden parametr, którego używam do wyeliminowania okna procesu

pProcess.StartInfo.CreateNoWindow = true;

pomaga to całkowicie ukryć czarne okno konsoli przed użytkownikiem, jeśli tego właśnie chcesz.


3
Zaoszczędził mi dużo bólu głowy. Dzięki.
Vivandiere

2
Podczas wywoływania „sc” musiałem również ustawić StartInfo.WindowStyle = ProcessWindowStyle.Hidden.
Pedro

90
// usage
const string ToolFileName = "example.exe";
string output = RunExternalExe(ToolFileName);

public string RunExternalExe(string filename, string arguments = null)
{
    var process = new Process();

    process.StartInfo.FileName = filename;
    if (!string.IsNullOrEmpty(arguments))
    {
        process.StartInfo.Arguments = arguments;
    }

    process.StartInfo.CreateNoWindow = true;
    process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
    process.StartInfo.UseShellExecute = false;

    process.StartInfo.RedirectStandardError = true;
    process.StartInfo.RedirectStandardOutput = true;
    var stdOutput = new StringBuilder();
    process.OutputDataReceived += (sender, args) => stdOutput.AppendLine(args.Data); // Use AppendLine rather than Append since args.Data is one line of output, not including the newline character.

    string stdError = null;
    try
    {
        process.Start();
        process.BeginOutputReadLine();
        stdError = process.StandardError.ReadToEnd();
        process.WaitForExit();
    }
    catch (Exception e)
    {
        throw new Exception("OS error while executing " + Format(filename, arguments)+ ": " + e.Message, e);
    }

    if (process.ExitCode == 0)
    {
        return stdOutput.ToString();
    }
    else
    {
        var message = new StringBuilder();

        if (!string.IsNullOrEmpty(stdError))
        {
            message.AppendLine(stdError);
        }

        if (stdOutput.Length != 0)
        {
            message.AppendLine("Std output:");
            message.AppendLine(stdOutput.ToString());
        }

        throw new Exception(Format(filename, arguments) + " finished with exit code = " + process.ExitCode + ": " + message);
    }
}

private string Format(string filename, string arguments)
{
    return "'" + filename + 
        ((string.IsNullOrEmpty(arguments)) ? string.Empty : " " + arguments) +
        "'";
}

3
Bardzo wyczerpujący przykład: Dzięki
ShahidAzim

2
Może chcesz zmienić moduł obsługi OutputDataReceived na stdOut.AppendLine ()
Paul Williams

3
Moim zdaniem jest to rozwiązanie o wiele bardziej kompleksowe niż przyjęta odpowiedź. Używam go teraz i nie użyłem zaakceptowanego, ale ten naprawdę wygląda na brak.
ProfK

1
Dzięki za process.StartInfo.RedirectStandardError = true;i if (process.ExitCode == 0)która nie ma zaakceptowanej odpowiedzi.
JohnB

12

Akceptowana odpowiedź na tej stronie ma słabość, która jest kłopotliwa w rzadkich sytuacjach. Istnieją dwa uchwyty plików, do których programy piszą zgodnie z konwencją, stdout i stderr. Jeśli po prostu czytasz pojedynczy uchwyt pliku, taki jak odpowiedź Ray, a program, który uruchamiasz, zapisuje wystarczającą ilość danych wyjściowych do stderr, zapełni bufor wyjściowy i blok stderr. Wtedy twoje dwa procesy są zakleszczone. Rozmiar bufora może wynosić 4K. Jest to niezwykle rzadkie w programach krótkotrwałych, ale jeśli masz długo działający program, który wielokrotnie wysyła do stderr, w końcu się to stanie. Debugowanie i śledzenie jest trudne.

Istnieje kilka dobrych sposobów, aby sobie z tym poradzić.

  1. Jednym ze sposobów jest uruchomienie cmd.exe zamiast programu i użycie argumentu / c do cmd.exe, aby wywołać program wraz z argumentem „2> i 1” do cmd.exe, aby nakazać mu połączenie stdout i stderr.

            var p = new Process();
            p.StartInfo.FileName = "cmd.exe";
            p.StartInfo.Arguments = "/c mycmd.exe 2>&1";
  2. Innym sposobem jest użycie modelu programowania, który odczytuje oba uchwyty jednocześnie.

            var p = new Process();
            p.StartInfo.FileName = "cmd.exe";
            p.StartInfo.Arguments = @"/c dir \windows";
            p.StartInfo.CreateNoWindow = true;
            p.StartInfo.RedirectStandardError = true;
            p.StartInfo.RedirectStandardOutput = true;
            p.StartInfo.RedirectStandardInput = false;
            p.OutputDataReceived += (a, b) => Console.WriteLine(b.Data);
            p.ErrorDataReceived += (a, b) => Console.WriteLine(b.Data);
            p.Start();
            p.BeginErrorReadLine();
            p.BeginOutputReadLine();
            p.WaitForExit();

2
Myślę, że lepiej odpowiada to oryginalne pytanie, ponieważ pokazuje, jak uruchomić polecenie CMD przez C # (nie plik).
TinyRacoon,

12
 System.Diagnostics.ProcessStartInfo psi =
   new System.Diagnostics.ProcessStartInfo(@"program_to_call.exe");
 psi.RedirectStandardOutput = true;
 psi.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
 psi.UseShellExecute = false;
 System.Diagnostics.Process proc = System.Diagnostics.Process.Start(psi); ////
 System.IO.StreamReader myOutput = proc.StandardOutput;
 proc.WaitForExit(2000);
 if (proc.HasExited)
  {
      string output = myOutput.ReadToEnd();
 }

Może dojść do impasu, gdy proces zapisuje dużo danych. Lepiej jest zacząć odczytywać dane, gdy proces nadal trwa.
JensG

6

Będziesz musiał używać ProcessStartInfoz RedirectStandardOutputwłączoną - wtedy możesz odczytać strumień wyjściowy. Możesz łatwiej użyć „>”, aby przekierować dane wyjściowe do pliku (przez system operacyjny), a następnie po prostu odczytać plik.

[edytuj: jak to zrobił Ray: +1]


10
To zmusza cię do napisania pliku w miejscu, do którego potrzebujesz pozwolenia, musisz znaleźć lokalizację i nazwę i nie możesz zapomnieć o usunięciu, gdy skończysz. Łatwiejszy w użyciu RedirectStandardOutput.
peSHIr

4

Jeśli nie masz nic przeciwko wprowadzeniu zależności, CliWrap może uprościć to dla Ciebie:

var cli = new Cli("target.exe");
var output = await cli.ExecuteAsync("arguments", "stdin");
var stdout = output.StandardOutput;

3

To może nie być najlepszy / najłatwiejszy sposób, ale może być opcją:

Po uruchomieniu z kodu dodaj „> output.txt”, a następnie wczytaj plik output.txt.


3

Możesz uruchomić dowolny program wiersza poleceń za pomocą klasy Process i ustawić właściwość StandardOutput instancji Process za pomocą utworzonego czytnika strumieniowego (na podstawie ciągu znaków lub lokalizacji pamięci). Po zakończeniu procesu możesz wykonać dowolną różnicę w tym strumieniu.


3

Może to być przydatne dla kogoś, kto próbuje wysłać zapytanie do lokalnej pamięci podręcznej ARP na komputerze / serwerze.

List<string[]> results = new List<string[]>();

        using (Process p = new Process())
        {
            p.StartInfo.CreateNoWindow = true;
            p.StartInfo.RedirectStandardOutput = true;
            p.StartInfo.UseShellExecute = false;
            p.StartInfo.Arguments = "/c arp -a";
            p.StartInfo.FileName = @"C:\Windows\System32\cmd.exe";
            p.Start();

            string line;

            while ((line = p.StandardOutput.ReadLine()) != null)
            {
                if (line != "" && !line.Contains("Interface") && !line.Contains("Physical Address"))
                {
                    var lineArr = line.Trim().Split(' ').Select(n => n).Where(n => !string.IsNullOrEmpty(n)).ToArray();
                    var arrResult = new string[]
                {
                   lineArr[0],
                   lineArr[1],
                   lineArr[2]
                };
                    results.Add(arrResult);
                }
            }

            p.WaitForExit();
        }

3

Polecenie uruchamiania w jednym wierszu:

new Process() { StartInfo = new ProcessStartInfo("echo", "Hello, World") }.Start();

Odczytaj wynik polecenia w najkrótszej ilości kodu reable:

    var cliProcess = new Process() {
        StartInfo = new ProcessStartInfo("echo", "Hello, World") {
            UseShellExecute = false,
            RedirectStandardOutput = true
        }
    };
    cliProcess.Start();
    string cliOut = cliProcess.StandardOutput.ReadToEnd();
    cliProcess.WaitForExit();
    cliProcess.Close();


2

Jeśli musisz także wykonać jakieś polecenie w cmd.exe, możesz wykonać następujące czynności:

// Start the child process.
Process p = new Process();
// Redirect the output stream of the child process.
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.FileName = "cmd.exe";
p.StartInfo.Arguments = "/C vol";
p.Start();
// Read the output stream first and then wait.
string output = p.StandardOutput.ReadToEnd();
p.WaitForExit();
Console.WriteLine(output);

Zwraca tylko wynik samego polecenia:

wprowadź opis zdjęcia tutaj

Możesz również użyć StandardInputzamiast StartInfo.Arguments:

// Start the child process.
Process p = new Process();
// Redirect the output stream of the child process.
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardInput = true;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.FileName = "cmd.exe";
p.Start();
// Read the output stream first and then wait.
p.StandardInput.WriteLine("vol");
p.StandardInput.WriteLine("exit");
string output = p.StandardOutput.ReadToEnd();
p.WaitForExit();
Console.WriteLine(output);

Wynik wygląda następująco:

wprowadź opis zdjęcia tutaj


0

Dla zabawy, oto moje kompletne rozwiązanie umożliwiające uzyskanie danych wyjściowych PYTHON - pod jednym kliknięciem przycisku - z raportowaniem błędów. Wystarczy dodać przycisk o nazwie „butPython” i etykietę „llHello” ...

    private void butPython(object sender, EventArgs e)
    {
        llHello.Text = "Calling Python...";
        this.Refresh();
        Tuple<String,String> python = GoPython(@"C:\Users\BLAH\Desktop\Code\Python\BLAH.py");
        llHello.Text = python.Item1; // Show result.
        if (python.Item2.Length > 0) MessageBox.Show("Sorry, there was an error:" + Environment.NewLine + python.Item2);
    }

    public Tuple<String,String> GoPython(string pythonFile, string moreArgs = "")
    {
        ProcessStartInfo PSI = new ProcessStartInfo();
        PSI.FileName = "py.exe";
        PSI.Arguments = string.Format("\"{0}\" {1}", pythonFile, moreArgs);
        PSI.CreateNoWindow = true;
        PSI.UseShellExecute = false;
        PSI.RedirectStandardError = true;
        PSI.RedirectStandardOutput = true;
        using (Process process = Process.Start(PSI))
            using (StreamReader reader = process.StandardOutput)
            {
                string stderr = process.StandardError.ReadToEnd(); // Error(s)!!
                string result = reader.ReadToEnd(); // What we want.
                return new Tuple<String,String> (result,stderr); 
            }
    }

0

Ponieważ większość odpowiedzi tutaj nie implementuje usingstatemant IDisposablei kilka innych rzeczy, które moim zdaniem mogą być niepotrzebne, dodam tę odpowiedź.

Dla C # 8.0

// Start a process with the filename or path with filename e.g. "cmd". Please note the 
//using statemant
using myProcess.StartInfo.FileName = "cmd";
// add the arguments - Note add "/c" if you want to carry out tge  argument in cmd and  
// terminate
myProcess.StartInfo.Arguments = "/c dir";
// Allows to raise events
myProcess.EnableRaisingEvents = true;
//hosted by the application itself to not open a black cmd window
myProcess.StartInfo.UseShellExecute = false;
myProcess.StartInfo.CreateNoWindow = true;
// Eventhander for data
myProcess.Exited += OnOutputDataRecived;
// Eventhandler for error
myProcess.ErrorDataReceived += OnErrorDataReceived;
// Eventhandler wich fires when exited
myProcess.Exited += OnExited;
// Starts the process
myProcess.Start();
//read the output before you wait for exit
myProcess.BeginOutputReadLine();
// wait for the finish - this will block (leave this out if you dont want to wait for 
// it, so it runs without blocking)
process.WaitForExit();

// Handle the dataevent
private void OnOutputDataRecived(object sender, DataReceivedEventArgs e)
{
    //do something with your data
    Trace.WriteLine(e.Data);
}

//Handle the error
private void OnErrorDataReceived(object sender, DataReceivedEventArgs e)
{        
    Trace.WriteLine(e.Data);
    //do something with your exception
    throw new Exception();
}    

// Handle Exited event and display process information.
private void OnExited(object sender, System.EventArgs e)
{
     Trace.WriteLine("Process exited");
}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.