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.
// 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 .
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();
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.
// 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) +
"'";
}
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();
}
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.
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";
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();
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]
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;
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.
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.
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();
}
Il existe une classe ProcessHelper dans PublicDomain code source libre qui pourrait vous intéresser.
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);
}
}