Comment: exécuter la ligne de commande en C #, obtenir les résultats STD OUT

472

Comment exécuter un programme de ligne de commande à partir de C # et récupérer les résultats STD OUT? Plus précisément, je veux exécuter DIFF sur deux fichiers sélectionnés par programme et écrire les résultats dans une zone de texte.

Aile
la source
2
Voir aussi stackoverflow.com/a/5367686/492 - il affiche les événements de sortie et les erreurs.
CAD bloke
Lié (mais sans capturer STDOUT): stackoverflow.com/questions/1469764
user202729

Réponses:

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

Le code provient de MSDN .

Ray Jezek
la source
8
Existe-t-il un moyen de le faire sans fichier de commandes? La chose est, je dois envoyer quelques paramètres à la commande. J'utilise xsd.exe <Assembly> / type: <ClassName>, donc je dois pouvoir définir à la fois l'assembly et le ClassName, puis exécuter la commande.
Carlo
26
Vous pouvez ajouter des arguments à votre appel via la {YourProcessObject}.StartInfo.Argumentschaîne.
patridge
5
Comment faire fonctionner le processus en tant qu'administrateur?
Saher Ahwal
5
J'ai rencontré un certain nombre de problèmes où mon processus, en utilisant ce code, s'arrête complètement car le processus a écrit suffisamment de données dans le p.StandardErrorflux. Lorsque le flux est plein, il semble que le processus s'arrête jusqu'à ce que les données soient consommées, je dois donc lire les deux StandardErroret StandardOutputafin de garantir qu'une tâche s'exécute correctement.
Ted Spence
5
Headup rapide à partir du compilateur c #: l'objet Process doit avoir la propriété UseShellExecute définie sur false afin de rediriger les flux d'E / S.
IbrarMumtaz
144

Voici un petit échantillon:

//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();
Jeremy
la source
2
+1 pour avoir montré comment ajouter des arguments à l'exécution d'un programme en ligne de commande (ce que la réponse acceptée n'a pas.)
Suman
104

Il y a un autre paramètre que j'ai trouvé utile, que j'utilise pour éliminer la fenêtre de processus

pProcess.StartInfo.CreateNoWindow = true;

cela aide à cacher complètement la fenêtre de console noire à l'utilisateur, si c'est ce que vous désirez.

Peter Du
la source
3
M'a sauvé beaucoup de maux de tête. Merci.
The Vivandiere
2
Lors de l'appel de "sc", je devais également définir 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) +
        "'";
}
Lu55
la source
3
Un exemple très complet, Merci
ShahidAzim
2
Pourrait vouloir changer le gestionnaire OutputDataReceived en stdOut.AppendLine ()
Paul Williams
3
À mon avis, il s'agit d'une solution beaucoup plus complète que la réponse acceptée. Je l'utilise maintenant, et je n'ai pas utilisé celui accepté, mais celui-ci semble vraiment manquer.
ProfK
1
Merci pour process.StartInfo.RedirectStandardError = true;et if (process.ExitCode == 0)quelle réponse acceptée n'a pas.
JohnB
12

La réponse acceptée sur cette page présente une faiblesse gênante dans de rares situations. Il existe deux descripteurs de fichiers sur lesquels les programmes écrivent par convention, stdout et stderr. Si vous venez de lire un seul descripteur de fichier tel que la réponse de Ray, et que le programme que vous démarrez écrit suffisamment de sortie dans stderr, il remplira le tampon et le bloc de sortie stderr. Vos deux processus sont alors bloqués. La taille du tampon peut être 4K. C'est extrêmement rare sur les programmes de courte durée, mais si vous avez un programme de longue durée qui sort à plusieurs reprises sur stderr, cela se produira éventuellement. C'est difficile à déboguer et à retrouver.

Il y a quelques bonnes façons de gérer cela.

  1. Une façon consiste à exécuter cmd.exe au lieu de votre programme et à utiliser l'argument / c à cmd.exe pour appeler votre programme avec l'argument "2> & 1" à cmd.exe pour lui dire de fusionner stdout et stderr.

            var p = new Process();
            p.StartInfo.FileName = "cmd.exe";
            p.StartInfo.Arguments = "/c mycmd.exe 2>&1";
  2. Une autre façon consiste à utiliser un modèle de programmation qui lit les deux poignées en même temps.

            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();
Cameron
la source
2
Je pense que cela répond mieux à la question d'origine, car il montre comment exécuter une commande CMD via C # (pas un fichier).
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();
 }
Jeff Mc
la source
Peut se bloquer lorsque le processus écrit beaucoup de données. Il est préférable de commencer à lire les données pendant que le processus s'exécute.
JensG
6

Vous devrez utiliser ProcessStartInfoavec RedirectStandardOutputactivé - alors vous pouvez lire le flux de sortie. Vous trouverez peut-être plus facile d'utiliser ">" pour rediriger la sortie vers un fichier (via le système d'exploitation), puis de simplement lire le fichier.

[modifier: comme ce que Ray a fait: +1]

Marc Gravell
la source
10
Cela vous oblige à écrire un fichier quelque part pour lequel vous avez besoin d'une autorisation, devez trouver un emplacement et un nom et ne devez pas oublier de le supprimer lorsque vous en avez terminé. Plus facile à utiliser en RedirectStandardOutputfait.
peSHIr
4

Si cela ne vous dérange pas d'introduire une dépendance, CliWrap peut simplifier cela pour vous:

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

Ce n'est peut-être pas le meilleur moyen / le plus simple, mais cela peut être une option:

Lorsque vous exécutez à partir de votre code, ajoutez "> output.txt", puis lisez le fichier output.txt.

Kon
la source
3

Vous pouvez lancer n'importe quel programme de ligne de commande à l'aide de la classe Process et définir la propriété StandardOutput de l'instance Process avec un lecteur de flux que vous créez (en fonction d'une chaîne ou d'un emplacement mémoire). Une fois le processus terminé, vous pouvez alors faire tout ce dont vous avez besoin sur ce flux.

pseudo
la source
3

Cela peut être utile pour quelqu'un si vous essayez d'interroger le cache ARP local sur un PC / serveur.

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();
        }
Kitson88
la source
3

Commande d'exécution sur une ligne:

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

Lire la sortie de la commande dans la plus petite quantité de code disponible:

    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();
Dan Stevens
la source
2

Si vous devez également exécuter une commande dans cmd.exe, vous pouvez effectuer les opérations suivantes:

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

Cela renvoie uniquement la sortie de la commande elle-même:

entrez la description de l'image ici

Vous pouvez également utiliser à la StandardInputplace de 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);

Le résultat ressemble à ceci:

entrez la description de l'image ici

Konard
la source
0

Juste pour le plaisir, voici ma solution complète pour obtenir une sortie PYTHON - sous un clic de bouton - avec un rapport d'erreur. Ajoutez simplement un bouton appelé "butPython" et une étiquette appelée "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); 
            }
    }
Dave
la source
0

Étant donné que la plupart des réponses ici usingn'implémentent pas le statemant pour IDisposableet d'autres choses qui, je pense, pourraient être nécessaires, j'ajouterai cette réponse.

Pour 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");
}
julien
la source