Process.start: bagaimana cara mendapatkan output?

306

Saya ingin menjalankan program baris perintah eksternal dari aplikasi Mono / .NET saya. Sebagai contoh, saya ingin menjalankan mencoder . Apa itu mungkin:

  1. Untuk mendapatkan keluaran shell baris perintah, dan menulisnya di kotak teks saya?
  2. Untuk mendapatkan nilai numerik untuk menampilkan bilah kemajuan dengan waktu yang berlalu?
Stighy
sumber

Jawaban:

458

Saat Anda membuat Processobjek Anda diatur dengan StartInfotepat:

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

kemudian mulailah proses dan baca darinya:

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

Anda dapat menggunakan int.Parse()atau int.TryParse()untuk mengkonversi string ke nilai numerik. Anda mungkin harus melakukan manipulasi string terlebih dahulu jika ada karakter numerik yang tidak valid dalam string yang Anda baca.

Ferruccio
sumber
4
Saya ingin tahu bagaimana Anda dapat menangani StandardError? BTW Saya sangat suka potongan kode ini! bagus dan bersih
codea
3
Terima kasih, Tapi saya rasa saya tidak jelas: Haruskah saya menambahkan loop lain untuk melakukannya?
codea
@codea - Begitu. Anda dapat membuat satu loop yang berakhir ketika kedua aliran mencapai EOF. Itu bisa menjadi sedikit rumit karena satu aliran pasti akan mengenai EOF terlebih dahulu dan Anda tidak ingin membacanya lagi. Anda juga bisa menggunakan dua loop di dua utas berbeda.
Ferruccio
1
Apakah lebih kuat untuk membaca sampai proses itu sendiri berakhir, daripada menunggu akhir aliran?
Gusdor
@ Goddor - Saya rasa tidak. Ketika proses berakhir, alirannya akan ditutup secara otomatis. Juga, suatu proses dapat menutup alirannya jauh sebelum berakhir.
Ferruccio
254

Anda dapat memproses output secara sinkron atau asinkron .

1. Contoh sinkron

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

Perhatikan bahwa lebih baik memproses output dan kesalahan : keduanya harus ditangani secara terpisah.

(*) Untuk beberapa perintah (di sini StartInfo.Arguments) Anda harus menambahkan /c arahan , jika tidak proses akan macet di WaitForExit().

2. Contoh asinkron

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

Jika Anda tidak perlu melakukan operasi yang rumit dengan output, Anda dapat mem-bypass metode OutputHandler, cukup menambahkan handler secara langsung sebaris:

//* Set your output and error (asynchronous) handlers
process.OutputDataReceived += (s, e) => Console.WriteLine(e.Data);
process.ErrorDataReceived += (s, e) => Console.WriteLine(e.Data);
T30
sumber
2
harus cinta async! Saya dapat menggunakan kode ini (dengan sedikit transkrip) di VB.net
Richard Barker
perhatikan 'string output = process.StandardOutput.ReadToEnd ();' dapat menghasilkan string besar jika ada banyak jalur output; contoh async, dan jawaban dari Ferruccio keduanya memproses keluaran baris demi baris.
Andrew Hill
5
Catatan: pendekatan (sinkron) pertama Anda tidak benar! Anda TIDAK boleh membaca StandardOutput dan StandardError secara serempak! itu akan menyebabkan kunci mati. setidaknya salah satu dari mereka harus async.
S.Serpooshan
6
Process.WaitForExit () adalah pemblokiran utas, dengan demikian sinkron. Bukan inti dari jawabannya, tapi saya pikir saya mungkin menambahkan ini. Tambahkan process.EnableRaisingEvents = true dan gunakan acara Exited untuk sepenuhnya asinkron.
Tom
Apakah tidak mungkin untuk mengarahkan ulang langsung? Saya menggunakan semua pewarnaan dari output sass?
Ini
14

Baiklah, bagi siapa saja yang ingin membaca Kesalahan dan Keluaran, tetapi mendapatkan jalan buntu dengan salah satu solusi, yang disediakan dalam jawaban lain (seperti saya), berikut adalah solusi yang saya buat setelah membaca penjelasan MSDN untuk StandardOutputproperti.

Jawaban didasarkan pada kode 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);
}
cubrman
sumber
Terima kasih telah menambahkan ini. Bisakah saya bertanya perintah apa yang Anda gunakan?
T30
Saya sedang mengembangkan aplikasi di c # yang dirancang untuk meluncurkan mysqldump.exe, perlihatkan kepada pengguna setiap pesan yang dihasilkan aplikasi, tunggu sampai selesai dan kemudian lakukan beberapa tugas lagi. Saya tidak mengerti apa jenis perintah yang Anda bicarakan? Seluruh pertanyaan ini adalah tentang meluncurkan proses dari c #.
cubrman
1
jika Anda menggunakan dua penangan yang terpisah, Anda tidak akan mendapatkan deadlock
Ovi
juga dalam contoh Anda, Anda membaca proses. Standar output hanya sekali ... tepat setelah Anda memulainya, tetapi orang ingin membacanya terus menerus saat proses sedang berjalan, bukan?
Ovi
7

Cara standar .NET melakukan ini adalah membaca dari aliran StandardOutput proses . Ada contoh dalam dokumen MSDN tertaut. Serupa, Anda dapat membaca dari StandardError , dan menulis ke StandardInput .

driis
sumber
4

Anda dapat menggunakan memori bersama untuk 2 proses berkomunikasi melalui, periksa MemoryMappedFile

Anda terutama akan membuat file yang dipetakan memori mmfdalam proses induk menggunakan pernyataan "menggunakan" kemudian buat proses kedua sampai berakhir dan biarkan ia menulis hasilnya ke mmfmenggunakan BinaryWriter, kemudian membaca hasil dari mmfmenggunakan proses induk, Anda juga bisa meneruskan mmfnama menggunakan argumen baris perintah atau kode keras itu.

pastikan ketika menggunakan file yang dipetakan dalam proses induk yang Anda buat proses anak tulis hasilnya ke file yang dipetakan sebelum file yang dipetakan dirilis dalam proses induk

Contoh: proses induk

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

Proses anak

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

untuk menggunakan sampel ini, Anda harus membuat solusi dengan 2 proyek di dalamnya, kemudian Anda mengambil hasil build dari proses anak dari% childDir% / bin / debug dan salin ke% parentDirectory% / bin / debug kemudian jalankan proyek induk

childDirdan parentDirectoryapakah nama folder proyek Anda di pc semoga berhasil :)

shakram02
sumber
1

Cara meluncurkan proses (seperti file bat, skrip perl, program konsol) dan memiliki output standar yang ditampilkan pada formulir 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();    

Anda dapat menemukan ProcessCallerdi tautan ini: Meluncurkan proses dan menampilkan output standarnya

abberdeen
sumber
1

Anda dapat mencatat keluaran proses menggunakan kode di bawah ini:

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) { 
}
SHUBHAM PATIDAR
sumber
1

Solusi yang bekerja untuk saya dalam win dan linux adalah mengikuti

// 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 [email protected]  --encrypt " + archivo;
            try{
                System.IO.File.WriteAllText(archivo, xml);                
                //String comando = "C:\\GnuPG\\bin\\gpg.exe --recipient [email protected] --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 };
            }
        }
Jorge Santos Neill
sumber