กระบวนการเริ่มต้น: วิธีการรับเอาท์พุท?


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 ได้อย่างไร BTW ฉันชอบตัวอย่างรหัสนี้จริงๆ! สวยและสะอาด
codea

3
ขอบคุณ แต่ฉันคิดว่าฉันไม่ชัดเจน: ฉันควรเพิ่มการวนซ้ำอีกครั้งเพื่อทำเช่นนั้นหรือไม่
codea

@codea - ฉันเห็น คุณสามารถสร้างหนึ่งลูปที่สิ้นสุดเมื่อสตรีมทั้งสองเข้าถึง EOF นั่นอาจซับซ้อนเล็กน้อยเพราะสตรีมหนึ่งจะกด EOF ก่อนและคุณไม่ต้องการอ่านอีกต่อไป คุณสามารถใช้สองลูปในสองเธรดที่แตกต่างกันได้
Ferruccio

1
การอ่านจนกว่าจะสิ้นสุดกระบวนการเองจะมีประสิทธิภาพมากกว่าการรอสตรีม?
Gusdor

@Gusdor - ฉันไม่คิดอย่างนั้น เมื่อกระบวนการสิ้นสุดลงสตรีมของมันจะถูกปิดโดยอัตโนมัติ นอกจากนี้กระบวนการอาจปิดสตรีมให้ยาวก่อนที่จะยุติ
Ferruccio

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
ต้องรัก async! ฉันสามารถใช้รหัสนี้ (โดยมีการถอดความเล็กน้อย) ใน VB.net
Richard Barker

หมายเหตุ 'สตริงเอาท์พุท = process.StandardOutput.ReadToEnd ();' อาจสร้างสตริงขนาดใหญ่หากมีหลายบรรทัดเอาท์พุท; ตัวอย่าง async และคำตอบจาก Ferruccio ทั้งคู่ประมวลผลเอาต์พุตบรรทัดต่อบรรทัด
Andrew Hill

5
หมายเหตุ: วิธีแรกของคุณ (ซิงโครนัส) ไม่ถูกต้อง! คุณไม่ควรอ่านทั้ง StandardOutput และ StandardError แบบซิงโครนัส! มันจะทำให้ล็อคตาย อย่างน้อยหนึ่งในนั้นจะต้องเป็นแบบซิงค์
S.Serpooshan

6
Process.WaitForExit () เป็นการบล็อกเธรดดังนั้นจึงซิงโครนัส ไม่ใช่จุดของคำตอบ แต่ฉันคิดว่าฉันอาจเพิ่มสิ่งนี้ เพิ่ม process.EnableRaisingEvents = true และใช้ประโยชน์จากเหตุการณ์ Exited ให้เป็นแบบอะซิงโครนัสอย่างสมบูรณ์
Tom

ไม่สามารถเปลี่ยนเส้นทางโดยตรงหรือไม่ ฉันใช้ colorization ทั้งหมดของ sass output หรือไม่
Ini

14

เอาล่ะสำหรับผู้ที่ต้องการอ่านข้อผิดพลาดและผลลัพธ์ แต่ได้รับการหยุดชะงักด้วยวิธีการแก้ปัญหาใด ๆ ที่ให้ไว้ในคำตอบอื่น ๆ (เช่นฉัน) ที่นี่เป็นวิธีการแก้ปัญหาที่ฉันสร้างขึ้นหลังจากอ่านคำอธิบาย MSDN สำหรับStandardOutputทรัพย์สิน

คำตอบขึ้นอยู่กับรหัสของ 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

ฉันกำลังพัฒนาแอพใน c # ที่ออกแบบมาเพื่อเปิด mysqldump.exe แสดงผู้ใช้ทุกข้อความที่แอปสร้างขึ้นรอให้มันเสร็จสิ้นจากนั้นจึงทำงานต่อไป ฉันไม่เข้าใจคำสั่งที่คุณกำลังพูดถึง? คำถามทั้งหมดนี้เกี่ยวกับการเปิดตัวกระบวนการจาก c #
cubrman

1
ถ้าคุณใช้ตัวจัดการสองตัวแยกกันคุณจะไม่ได้รับ deadlocks
Ovi

ในตัวอย่างของคุณคุณอ่านกระบวนการมาตรฐานเอาท์พุทเพียงครั้งเดียว ... ทันทีหลังจากที่คุณเริ่มต้น แต่หนึ่งต้องการอ่านอย่างต่อเนื่องในขณะที่กระบวนการกำลังทำงานอยู่ไม่?
Ovi

7

วิธีการ. NET มาตรฐานในการทำเช่นนี้คือการอ่านจากสตรีมกระบวนการ ' StandardOutput มีตัวอย่างในเอกสาร MSDN ที่เชื่อมโยง ที่คล้ายกันคุณสามารถอ่านได้จากStandardErrorและเขียนไปStandardInput


5
  1. เป็นไปได้ที่จะรับเอาต์พุตเชลล์บรรทัดคำสั่งของกระบวนการตามที่อธิบายไว้ที่นี่: http://www.c-sharpcorner.com/UploadFile/edwinlima/SystemDiagnosticProcess12052005035444AM/SystemDiagnosticProcess.aspx

  2. ขึ้นอยู่กับ mencoder ถ้าเป็นสถานะนี้ในบรรทัดคำสั่งใช่แล้ว :)


4

คุณสามารถใช้หน่วยความจำที่ใช้ร่วมกันสำหรับ 2 กระบวนการในการสื่อสารผ่านตรวจสอบ MemoryMappedFile

คุณส่วนใหญ่จะสร้างไฟล์หน่วยความจำที่แมปmmfในกระบวนการหลักโดยใช้คำสั่ง "ใช้" แล้วสร้างกระบวนการที่สองจนกว่ามันจะยุติและปล่อยให้มันเขียนผลการmmfใช้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เป็นชื่อโฟลเดอร์ของโครงการของคุณในพีซีโชคดี :)


1

วิธีเรียกใช้โปรเซส (เช่นไฟล์ bat, perl script, โปรแกรมคอนโซล) และแสดงเอาต์พุตมาตรฐานในรูปแบบ 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 คือ folling

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