Как захватить вывод команды оболочки в C#?
резюме:
- запрос реестра на удаленной машине
- выходных данных для использования в приложении
- должен быть в csharp
- до сих пор все используемые методы могут запрашивать только на локальном компьютере
- любая надежда очень ценится
полный вопрос:
мне нужно найти способ запустить команду командной строки в csharp и захватить ее вывод. Я знаю, как это сделать в Perl, ниже приведен код, который я бы использовал в язык Perl.
#machine to check
my $pc = $_[0];
#create location of registry query
my $machine = "\".$pc."HKEY_USERS";
#run registry query
my @regQuery= `REG QUERY $machine`;
любые предложения о том, как это сделать в csharp, будут приветствоваться. До сих пор я пытался использовать RegistryKey OurKey = Registry.Метод пользователей и он отлично работает, но я не могу запросить реестр на удаленной машине.
пожалуйста, дайте мне знать, если вам нужно больше информации.
решение: (Спасибо @Robaticus)
private void reg(string host)
{
string build = "QUERY \" + host + "HKEY_USERS";
string parms = @build;
string output = "";
string error = string.Empty;
ProcessStartInfo psi = new ProcessStartInfo("reg.exe", parms);
psi.RedirectStandardOutput = true;
psi.RedirectStandardError = true;
psi.WindowStyle = System.Diagnostics.ProcessWindowStyle.Normal;
psi.UseShellExecute = false;
System.Diagnostics.Process reg;
reg = System.Diagnostics.Process.Start(psi);
using (System.IO.StreamReader myOutput = reg.StandardOutput)
{
output = myOutput.ReadToEnd();
}
using (System.IO.StreamReader myError = reg.StandardError)
{
error = myError.ReadToEnd();
}
Output.AppendText(output + "n");
}
5 ответов
возможно, вам придется немного изменить это, но вот некоторые (слегка измененные из исходного) кода, который перенаправляет stdout и stderr для процесса:
string parms = @"QUERY \machine\HKEY_USERS";
string output = "";
string error = string.Empty;
ProcessStartInfo psi = new ProcessStartInfo("reg.exe", parms);
psi.RedirectStandardOutput = true;
psi.RedirectStandardError = true;
psi.WindowStyle = System.Diagnostics.ProcessWindowStyle.Normal;
psi.UseShellExecute = false;
System.Diagnostics.Process reg;
reg = System.Diagnostics.Process.Start(psi);
using (System.IO.StreamReader myOutput = reg.StandardOutput)
{
output = myOutput.ReadToEnd();
}
using(System.IO.StreamReader myError = reg.StandardError)
{
error = myError.ReadToEnd();
}
практически все, что вы можете запустить в командной строке, вы можете запустить в программе C# с аналогичными ограничениями. Есть несколько способов сделать это, один - через команды асинхронного процесса, как я показываю в своем блог. Вы просто пишете и читаете в командной строке активным образом. Отсюда просто выясните, чего вы хотите достичь и как это сделать с помощью командной строки. Затем подключите его к программе
class Program
{
static void Main(string[] args)
{
LaunchCommandAsProcess cmd = new LaunchCommandAsProcess();
cmd.OutputReceived += new LaunchCommandAsProcess.OutputEventHandler(launch_OutputReceived);
cmd.SendCommand("help");
cmd.SendCommand("ipconfig");
cmd.SyncClose();
}
/// Outputs normal and error output from the command prompt.
static void launch_OutputReceived(object sendingProcess, EventArgsForCommand e)
{
Console.WriteLine(e.OutputData);
}
}
Как вы можете видеть, вы просто создаете экземпляр класса, обработайте событие вывода и начните писать команды так же, как вы печатали в командной строке.
вот как это работает:
public class LaunchCommandAsProcess
{
public delegate void OutputEventHandler(object sendingProcess, EventArgsForCommand e);
public event OutputEventHandler OutputReceived;
private StreamWriter stdIn;
private Process p;
public void SendCommand(string command)
{
stdIn.WriteLine(command);
}
public LaunchCommandAsProcess()
{
p = new Process();
p.StartInfo.FileName = @"C:\Windows\System32\cmd.exe";
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardInput = true;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.RedirectStandardError = true;
p.StartInfo.CreateNoWindow = true;
p.Start();
stdIn = p.StandardInput;
p.OutputDataReceived += Process_OutputDataReceived;
p.ErrorDataReceived += Process_OutputDataReceived;
p.BeginOutputReadLine();
p.BeginErrorReadLine();
}
///
/// Raises events when output data has been received. Includes normal and error output.
///
/// /// private void Process_OutputDataReceived(object sendingProcess, DataReceivedEventArgs outLine)
{
if (outLine.Data == null)
return;
else
{
if (OutputReceived != null)
{
EventArgsForCommand e = new EventArgsForCommand();
e.OutputData = outLine.Data;
OutputReceived(this, e);
}
}
}
///
/// Synchronously closes the command promp.
///
public void SyncClose()
{
stdIn.WriteLine("exit");
p.WaitForExit();
p.Close();
}
///
/// Asynchronously closees the command prompt.
///
public void AsyncClose()
{
stdIn.WriteLine("exit");
p.Close();
}
}
public class EventArgsForCommand : EventArgs
{
public string OutputData { get; internal set; }
}
вот класс, который я использую. Он адаптирован из кода, который я нашел в блоге некоторое время назад, но с различными другими модификациями.
using System;
using System.Diagnostics;
using System.Text;
using System.Threading;
namespace SonomaTechnologyInc {
/// <summary>
/// Utility class for working with command-line programs.
/// </summary>
public class Subprocess {
private Subprocess() { }
/// <summary>
/// Executes a command-line program, specifying a maximum time to wait
/// for it to complete.
/// </summary>
/// <param name="command">
/// The path to the program executable.
/// </param>
/// <param name="args">
/// The command-line arguments for the program.
/// </param>
/// <param name="timeout">
/// The maximum time to wait for the subprocess to complete, in milliseconds.
/// </param>
/// <returns>
/// A <see cref="SubprocessResult"/> containing the results of
/// running the program.
/// </returns>
public static SubprocessResult RunProgram(string command, string args, int timeout) {
bool timedOut = false;
ProcessStartInfo pinfo = new ProcessStartInfo(command);
pinfo.Arguments = args;
pinfo.UseShellExecute = false;
pinfo.CreateNoWindow = true;
//pinfo.WorkingDirectory = ?
pinfo.RedirectStandardOutput = true;
pinfo.RedirectStandardError = true;
Process subprocess = Process.Start(pinfo);
ProcessStream processStream = new ProcessStream();
try {
processStream.Read(subprocess);
subprocess.WaitForExit(timeout);
processStream.Stop();
if(!subprocess.HasExited) {
// OK, we waited until the timeout but it still didn't exit; just kill the process now
timedOut = true;
try {
subprocess.Kill();
processStream.Stop();
} catch { }
subprocess.WaitForExit();
}
} catch(Exception ex) {
subprocess.Kill();
processStream.Stop();
throw ex;
} finally {
processStream.Stop();
}
TimeSpan duration = subprocess.ExitTime - subprocess.StartTime;
float executionTime = (float) duration.TotalSeconds;
SubprocessResult result = new SubprocessResult(
executionTime,
processStream.StandardOutput.Trim(),
processStream.StandardError.Trim(),
subprocess.ExitCode,
timedOut);
return result;
}
}
/// <summary>
/// Represents the result of executing a command-line program.
/// </summary>
public class SubprocessResult {
readonly float executionTime;
readonly string stdout;
readonly string stderr;
readonly int exitCode;
readonly bool timedOut;
internal SubprocessResult(float executionTime, string stdout, string stderr, int exitCode, bool timedOut) {
this.executionTime = executionTime;
this.stdout = stdout;
this.stderr = stderr;
this.exitCode = exitCode;
this.timedOut = timedOut;
}
/// <summary>
/// Gets the total wall time that the subprocess took, in seconds.
/// </summary>
public float ExecutionTime {
get { return executionTime; }
}
/// <summary>
/// Gets the output that the subprocess wrote to its standard output stream.
/// </summary>
public string Stdout {
get { return stdout; }
}
/// <summary>
/// Gets the output that the subprocess wrote to its standard error stream.
/// </summary>
public string Stderr {
get { return stderr; }
}
/// <summary>
/// Gets the subprocess's exit code.
/// </summary>
public int ExitCode {
get { return exitCode; }
}
/// <summary>
/// Gets a flag indicating whether the subprocess was aborted because it
/// timed out.
/// </summary>
public bool TimedOut {
get { return timedOut; }
}
}
internal class ProcessStream {
/*
* Class to get process stdout/stderr streams
* Author: SeemabK (seemabk@yahoo.com)
* Usage:
//create ProcessStream
ProcessStream myProcessStream = new ProcessStream();
//create and populate Process as needed
Process myProcess = new Process();
myProcess.StartInfo.FileName = "myexec.exe";
myProcess.StartInfo.Arguments = "-myargs";
//redirect stdout and/or stderr
myProcess.StartInfo.UseShellExecute = false;
myProcess.StartInfo.RedirectStandardOutput = true;
myProcess.StartInfo.RedirectStandardError = true;
//start Process
myProcess.Start();
//connect to ProcessStream
myProcessStream.Read(ref myProcess);
//wait for Process to end
myProcess.WaitForExit();
//get the captured output :)
string output = myProcessStream.StandardOutput;
string error = myProcessStream.StandardError;
*/
private Thread StandardOutputReader;
private Thread StandardErrorReader;
private Process RunProcess;
private string _StandardOutput = "";
private string _StandardError = "";
public string StandardOutput {
get { return _StandardOutput; }
}
public string StandardError {
get { return _StandardError; }
}
public ProcessStream() {
Init();
}
public void Read(Process process) {
try {
Init();
RunProcess = process;
if(RunProcess.StartInfo.RedirectStandardOutput) {
StandardOutputReader = new Thread(new ThreadStart(ReadStandardOutput));
StandardOutputReader.Start();
}
if(RunProcess.StartInfo.RedirectStandardError) {
StandardErrorReader = new Thread(new ThreadStart(ReadStandardError));
StandardErrorReader.Start();
}
int TIMEOUT = 1 * 60 * 1000; // one minute
if(StandardOutputReader != null)
StandardOutputReader.Join(TIMEOUT);
if(StandardErrorReader != null)
StandardErrorReader.Join(TIMEOUT);
} catch { }
}
private void ReadStandardOutput() {
if(RunProcess == null) return;
try {
StringBuilder sb = new StringBuilder();
string line = null;
while((line = RunProcess.StandardOutput.ReadLine()) != null) {
sb.Append(line);
sb.Append(Environment.NewLine);
}
_StandardOutput = sb.ToString();
} catch { }
}
private void ReadStandardError() {
if(RunProcess == null) return;
try {
StringBuilder sb = new StringBuilder();
string line = null;
while((line = RunProcess.StandardError.ReadLine()) != null) {
sb.Append(line);
sb.Append(Environment.NewLine);
}
_StandardError = sb.ToString();
} catch { }
}
private void Init() {
_StandardError = "";
_StandardOutput = "";
RunProcess = null;
Stop();
}
public void Stop() {
try { if(StandardOutputReader != null) StandardOutputReader.Abort(); } catch { }
try { if(StandardErrorReader != null) StandardErrorReader.Abort(); } catch { }
StandardOutputReader = null;
StandardErrorReader = null;
}
}
}
это не ответ на вопрос, но Registry.OpenRemoteBaseKey
метод подключается к реестру другой машины таким же образом, что
вы можете захватить StandardOutput и StandardError с помощью системы.Диагностика.Класс процессов.
http://msdn.microsoft.com/en-us/library/system.diagnostics.process.aspx
обязательно прочитайте раздел "Примечания" документации. Некоторые свойства класса process должны быть установлены правильно, чтобы StandardOutput был доступен (например, UseShellExecute должен быть установлен в false).