Process.start:出力を取得する方法?


306

Mono / .NETアプリから外部コマンドラインプログラムを実行したいのですが。たとえば、mencoderを実行します。出来ますか:

  1. コマンドラインシェル出力を取得して、テキストボックスに書き込むには?
  2. 経過時間とともに進行状況バーを表示する数値を取得するには?

回答:


458

ProcessオブジェクトセットをStartInfo適切に作成すると、次のようになります。

var proc = new Process 
{
    StartInfo = new ProcessStartInfo
    {
        FileName = "program.exe",
        Arguments = "command line arguments to your executable",
        UseShellExecute = false,
        RedirectStandardOutput = true,
        CreateNoWindow = true
    }
};

次に、プロセスを開始し、それから読み取ります。

proc.Start();
while (!proc.StandardOutput.EndOfStream)
{
    string line = proc.StandardOutput.ReadLine();
    // do something with line
}

int.Parse()またはint.TryParse()を使用して、文字列を数値に変換できます。読み取る文字列に無効な数字がある場合は、最初に文字列操作を行う必要がある場合があります。


4
どのようにStandardErrorを処理できるのか疑問に思いました。ところで私はこのコードスニペットが本当に好きです!素敵できれい。
codea 2013年

3
ありがとう、しかし私ははっきりしていなかったと思います:そうするために別のループを追加する必要がありますか?
codea 2013年

@codea-なるほど。両方のストリームがEOFに達したときに終了する1つのループを作成できます。1つのストリームは必然的に最初にEOFにヒットし、それからそれ以上読みたくないので、少し複雑になる可能性があります。2つの異なるスレッドで2つのループを使用することもできます。
Ferruccio 2013年

1
ストリームの終了を待つのではなく、プロセス自体が終了するまで読み取る方が堅牢ですか?
Gusdor 2014年

@Gusdor-そうは思いません。プロセスが終了すると、そのストリームは自動的に閉じられます。また、プロセスは、終了するかなり前にストリームを閉じる場合があります。
Ferruccio 2014年

254

出力は同期または非同期で処理できます。

1.同期の例

static void runCommand()
{
    Process process = new Process();
    process.StartInfo.FileName = "cmd.exe";
    process.StartInfo.Arguments = "/c DIR"; // Note the /c command (*)
    process.StartInfo.UseShellExecute = false;
    process.StartInfo.RedirectStandardOutput = true;
    process.StartInfo.RedirectStandardError = true;
    process.Start();
    //* Read the output (or the error)
    string output = process.StandardOutput.ReadToEnd();
    Console.WriteLine(output);
    string err = process.StandardError.ReadToEnd();
    Console.WriteLine(err);
    process.WaitForExit();
}

それは両方処理する方が良いでしょうことを出力し、エラーを:彼らは別々に処理されなければなりません。

(*)一部のコマンド(ここStartInfo.Arguments)では、/c ディレクティブを追加する必要があります。追加しないと、プロセスがでフリーズしますWaitForExit()

2.非同期の例

static void runCommand() 
{
    //* Create your Process
    Process process = new Process();
    process.StartInfo.FileName = "cmd.exe";
    process.StartInfo.Arguments = "/c DIR";
    process.StartInfo.UseShellExecute = false;
    process.StartInfo.RedirectStandardOutput = true;
    process.StartInfo.RedirectStandardError = true;
    //* Set your output and error (asynchronous) handlers
    process.OutputDataReceived += new DataReceivedEventHandler(OutputHandler);
    process.ErrorDataReceived += new DataReceivedEventHandler(OutputHandler);
    //* Start process and handlers
    process.Start();
    process.BeginOutputReadLine();
    process.BeginErrorReadLine();
    process.WaitForExit();
}

static void OutputHandler(object sendingProcess, DataReceivedEventArgs outLine) 
{
    //* Do your stuff with the output (write to console/log/StringBuilder)
    Console.WriteLine(outLine.Data);
}

出力で複雑な操作を行う必要がない場合は、ハンドラを直接インラインに追加するだけで、OutputHandlerメソッドをバイパスできます。

//* Set your output and error (asynchronous) handlers
process.OutputDataReceived += (s, e) => Console.WriteLine(e.Data);
process.ErrorDataReceived += (s, e) => Console.WriteLine(e.Data);

2
非同期が大好きです!VB.netでこのコードを(少し文字起こしして)使用することができました
Richard Barker

'string output = process.StandardOutput.ReadToEnd();'に注意してください 出力の行が多い場合、大きな文字列が生成される可能性があります。非同期の例とFerruccioからの回答はどちらも、出力を1行ずつ処理します。
アンドリューヒル

5
注:最初の(同期)アプローチは正しくありません!StandardOutputとStandardErrorの両方を同時に読み取ることはできません。デッドロックが発生します。それらの少なくとも1つは非同期でなければなりません。
S.Serpooshan

6
Process.WaitForExit()はスレッドブロックであるため、同期しています。答えのポイントではありませんが、私はこれを追加するかもしれないと思いました。process.EnableRaisingEvents = trueを追加し、Exitedイベントを利用して完全に非同期にします。
トム

直接リダイレクトすることはできませんか?sass出力のすべてのカラー化を使用しますか?
iniファイル

14

エラーと出力の両方を読みたいが、他の回答(私のような)で提供されている解決策のいずれかでデッドロック発生する人のために、これはStandardOutputプロパティに関するMSDNの説明を読んだ後に構築した解決策です。

回答はT30のコードに基づいています。

static void runCommand()
{
    //* Create your Process
    Process process = new Process();
    process.StartInfo.FileName = "cmd.exe";
    process.StartInfo.Arguments = "/c DIR";
    process.StartInfo.UseShellExecute = false;
    process.StartInfo.RedirectStandardOutput = true;
    process.StartInfo.RedirectStandardError = true;
    //* Set ONLY ONE handler here.
    process.ErrorDataReceived += new DataReceivedEventHandler(ErrorOutputHandler);
    //* Start process
    process.Start();
    //* Read one element asynchronously
    process.BeginErrorReadLine();
    //* Read the other one synchronously
    string output = process.StandardOutput.ReadToEnd();
    Console.WriteLine(output);
    process.WaitForExit();
}

static void ErrorOutputHandler(object sendingProcess, DataReceivedEventArgs outLine) 
{
    //* Do your stuff with the output (write to console/log/StringBuilder)
    Console.WriteLine(outLine.Data);
}

これを追加していただきありがとうございます。使用していたコマンドを聞いてもいいですか?
T30

私はmysqldump.exeを起動するように設計されたアプリをc#で開発しています。アプリが生成するすべてのメッセージをユーザーに表示し、アプリが完了するのを待ってから、さらにいくつかのタスクを実行します。どのようなコマンドについて話しているのか理解できませんか?この質問全体は、c#からプロセスを起動することに関するものです。
cubrman 2017

1
2つの別個のハンドラーを使用する場合、デッドロックは発生しません
Ovi

また、あなたの例では、process.StandardOutputを一度だけ読み取ります...起動直後に、プロセスの実行中に継続的に読み取りたいですか?
Ovi

7

これを行う標準の.NETの方法は、プロセスのStandardOutputストリームから読み取ることです。リンクされているMSDNドキュメントに例があります。同様に、あなたはから読み取ることができますはStandardError、およびへの書き込みStandardInput



4

2つのプロセスが共有メモリを使用して通信することができます。チェックアウトしてください。 MemoryMappedFile

mmfに、「using」ステートメントを使用して親プロセスでメモリマップファイルを作成し、2番目のプロセスを終了するまで作成し、結果をmmfusingに書き込みBinaryWriter、次にmmf親プロセスを使用して結果を読み取ります。mmfコマンドライン引数を使用して名前を渡すか、ハードコードします。

親プロセスでマップされたファイルを使用する場合、マップされたファイルが親プロセスで解放される前に、子プロセスに結果をマップされたファイルに書き込むようにしてください

例:親プロセス

    private static void Main(string[] args)
    {
        using (MemoryMappedFile mmf = MemoryMappedFile.CreateNew("memfile", 128))
        {
            using (MemoryMappedViewStream stream = mmf.CreateViewStream())
            {
                BinaryWriter writer = new BinaryWriter(stream);
                writer.Write(512);
            }

            Console.WriteLine("Starting the child process");
            // Command line args are separated by a space
            Process p = Process.Start("ChildProcess.exe", "memfile");

            Console.WriteLine("Waiting child to die");

            p.WaitForExit();
            Console.WriteLine("Child died");

            using (MemoryMappedViewStream stream = mmf.CreateViewStream())
            {
                BinaryReader reader = new BinaryReader(stream);
                Console.WriteLine("Result:" + reader.ReadInt32());
            }
        }
        Console.WriteLine("Press any key to continue...");
        Console.ReadKey();
    }

子プロセス

    private static void Main(string[] args)
    {
        Console.WriteLine("Child process started");
        string mmfName = args[0];

        using (MemoryMappedFile mmf = MemoryMappedFile.OpenExisting(mmfName))
        {
            int readValue;
            using (MemoryMappedViewStream stream = mmf.CreateViewStream())
            {
                BinaryReader reader = new BinaryReader(stream);
                Console.WriteLine("child reading: " + (readValue = reader.ReadInt32()));
            }
            using (MemoryMappedViewStream input = mmf.CreateViewStream())
            {
                BinaryWriter writer = new BinaryWriter(input);
                writer.Write(readValue * 2);
            }
        }

        Console.WriteLine("Press any key to continue...");
        Console.ReadKey();
    }

このサンプルを使用するには、内部に2つのプロジェクトがあるソリューションを作成する必要があります。次に、子プロセスのビルド結果を%childDir%/ bin / debugから取得し、%parentDirectory%/ bin / debugにコピーして、親プロジェクト

childDir そして parentDirectory、PCの幸運のあなたのプロジェクトのフォルダ名です:)


1

プロセス(batファイル、perlスクリプト、コンソールプログラムなど)を起動し、その標準出力をWindowsフォームに表示する方法:

processCaller = new ProcessCaller(this);
//processCaller.FileName = @"..\..\hello.bat";
processCaller.FileName = @"commandline.exe";
processCaller.Arguments = "";
processCaller.StdErrReceived += new DataReceivedHandler(writeStreamInfo);
processCaller.StdOutReceived += new DataReceivedHandler(writeStreamInfo);
processCaller.Completed += new EventHandler(processCompletedOrCanceled);
processCaller.Cancelled += new EventHandler(processCompletedOrCanceled);
// processCaller.Failed += no event handler for this one, yet.

this.richTextBox1.Text = "Started function.  Please stand by.." + Environment.NewLine;

// the following function starts a process and returns immediately,
// thus allowing the form to stay responsive.
processCaller.Start();    

ProcessCallerこのリンクで見つけることができます:プロセスの起動とその標準出力の表示


1

以下のコードを使用して、プロセスの出力をログに記録できます。

ProcessStartInfo pinfo = new ProcessStartInfo(item);
pinfo.CreateNoWindow = false;
pinfo.UseShellExecute = true;
pinfo.RedirectStandardOutput = true;
pinfo.RedirectStandardInput = true;
pinfo.RedirectStandardError = true;
pinfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Normal;
var p = Process.Start(pinfo);
p.WaitForExit();
Process process = Process.Start(new ProcessStartInfo((item + '>' + item + ".txt"))
{
    UseShellExecute = false,
    RedirectStandardOutput = true
});
process.WaitForExit();
string output = process.StandardOutput.ReadToEnd();
if (process.ExitCode != 0) { 
}

1

勝利とLinuxで私のために働いた解決策は次のとおりです

// GET api/values
        [HttpGet("cifrado/{xml}")]
        public ActionResult<IEnumerable<string>> Cifrado(String xml)
        {
            String nombreXML = DateTime.Now.ToString("ddMMyyyyhhmmss").ToString();
            String archivo = "/app/files/"+nombreXML + ".XML";
            String comando = " --armor --recipient bibankingprd@bi.com.gt  --encrypt " + archivo;
            try{
                System.IO.File.WriteAllText(archivo, xml);                
                //String comando = "C:\\GnuPG\\bin\\gpg.exe --recipient licorera@local.com --armor --encrypt C:\\Users\\Administrador\\Documents\\pruebas\\nuevo.xml ";
                ProcessStartInfo startInfo = new ProcessStartInfo() {FileName = "/usr/bin/gpg",  Arguments = comando }; 
                Process proc = new Process() { StartInfo = startInfo, };
                proc.StartInfo.RedirectStandardOutput = true;
                proc.StartInfo.RedirectStandardError = true;
                proc.Start();
                proc.WaitForExit();
                Console.WriteLine(proc.StandardOutput.ReadToEnd());
                return new string[] { "Archivo encriptado", archivo + " - "+ comando};
            }catch (Exception exception){
                return new string[] { archivo, "exception: "+exception.ToString() + " - "+ comando };
            }
        }
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.