web-dev-qa-db-fra.com

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

Comment exécuter un programme de ligne de commande à partir de C # et récupérer les résultats de 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.

442
Wing
// 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 .

492
Ray Jezek

Voici un échantillon rapide:

//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();
137
Jeremy

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 la console noire à l'utilisateur, si c'est ce que vous désirez.

98
Peter Du
// 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) +
        "'";
}
85
Lu55
 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();
 }
13
Jeff Mc

La réponse acceptée sur cette page a une faiblesse qui est gênant dans de rares cas. Il existe deux descripteurs de fichiers sur lesquels les programmes écrivent par convention, stdout et stderr. Si vous ne lisez qu'un seul fichier, tel que la réponse de Ray, et que le programme que vous démarrez écrit suffisamment en sortie sur stderr, le tampon et le bloc stderr en sortie seront remplis. Ensuite, vos deux processus sont dans l'impasse. La taille de la mémoire tampon peut être 4K. Ceci est extrêmement rare sur les programmes de courte durée, mais si vous avez un programme de longue durée qui génère plusieurs sorties vers stderr, cela se produira éventuellement. C'est difficile à déboguer et à suivre.

Il y a deux bons moyens de gérer cela.

  1. Une solution consiste à exécuter cmd.exe à la place de votre programme et à utiliser l’argument/c de cmd.exe pour appeler votre programme, ainsi que l’argument "2> & 1" de cmd.exe pour lui demander 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 méthode consiste à utiliser un modèle de programmation qui lit les deux descripteurs 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();
    
8
Cameron

Vous devrez utiliser ProcessStartInfo avec RedirectStandardOutput activé - vous pourrez alors 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]

6
Marc Gravell

Si cela ne vous dérange pas d'introduire une dépendance, CliWrap peut vous simplifier la tâche:

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

Ce n'est peut-être pas la meilleure façon, mais une option:

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

3
Kon

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 le diff dont vous avez besoin sur ce flux.

3
Nick

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

Il existe une classe ProcessHelper dans PublicDomain code source libre qui pourrait vous intéresser.

2
Shaik

Juste pour le plaisir, voici ma solution complète pour obtenir PYTHON une sortie - en un clic de souris - 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); 
            }
    }
0
Dave