Process.start: làm thế nào để có được đầu ra?


306

Tôi muốn chạy một chương trình dòng lệnh bên ngoài từ ứng dụng Mono / .NET của tôi. Ví dụ, tôi muốn chạy mencoder . Có thể:

  1. Để có được đầu ra shell dòng lệnh, và viết nó vào hộp văn bản của tôi?
  2. Để có được giá trị số để hiển thị một thanh tiến trình với thời gian trôi qua?

Câu trả lời:


458

Khi bạn tạo bộ Processđối tượng của mình StartInfomột cách thích hợp:

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

sau đó bắt đầu quá trình và đọc từ nó:

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

Bạn có thể sử dụng int.Parse()hoặc int.TryParse()để chuyển đổi các chuỗi thành giá trị số. Trước tiên, bạn có thể phải thực hiện một số thao tác chuỗi nếu có các ký tự số không hợp lệ trong chuỗi bạn đọc.


4
Tôi đã tự hỏi làm thế nào bạn có thể đối phó với StandardError? BTW Tôi thực sự thích đoạn mã này! Đẹp và sạch sẽ.
codea

3
Cảm ơn, nhưng tôi nghĩ rằng tôi đã không rõ ràng: tôi có nên thêm một vòng lặp khác để làm như vậy không?
codea

@codea - Tôi hiểu rồi. Bạn có thể tạo một vòng lặp kết thúc khi cả hai luồng đạt EOF. Điều đó có thể hơi phức tạp vì một luồng chắc chắn sẽ chạm vào EOF trước và bạn không muốn đọc từ đó nữa. Bạn cũng có thể sử dụng hai vòng trong hai luồng khác nhau.
Ferruccio

1
Có mạnh mẽ hơn để đọc cho đến khi quá trình tự chấm dứt, thay vì chờ kết thúc luồng?
Gusdor

@Gusdor - Tôi không nghĩ vậy. Khi quá trình kết thúc, các luồng của nó sẽ tự động bị đóng. Ngoài ra, một quá trình có thể đóng luồng của nó rất lâu trước khi nó kết thúc.
Ferruccio

254

Bạn có thể xử lý đầu ra của mình một cách đồng bộ hoặc không đồng bộ .

1. Ví dụ đồng bộ

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

Lưu ý rằng tốt hơn là xử lý cả đầu ralỗi : chúng phải được xử lý riêng.

(*) Đối với một số lệnh (ở đây StartInfo.Arguments), bạn phải thêm các /c chỉ thị , nếu không đóng băng quá trình trong WaitForExit().

2. Ví dụ không đồng bộ

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

Nếu bạn không cần thực hiện các thao tác phức tạp với đầu ra, bạn có thể bỏ qua phương thức OutputHandler, chỉ cần thêm các trình xử lý trực tiếp:

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

2
phải yêu async! Tôi đã có thể sử dụng mã này (với một chút phiên âm) trong VB.net
Richard Barker

lưu ý 'chuỗi đầu ra = process.St ChuẩnOutput.ReadToEnd ();' có thể tạo ra một chuỗi lớn nếu có nhiều dòng đầu ra; ví dụ async và câu trả lời từ Ferruccio đều xử lý dòng đầu ra theo từng dòng.
Đồi Andrew

5
Lưu ý: cách tiếp cận đầu tiên (đồng bộ) của bạn là không chính xác! Bạn KHÔNG nên đọc đồng thời cả StandardOutput và StandardError! nó sẽ gây ra khóa chết. ít nhất một trong số chúng phải không đồng bộ.
S.Serpoo Sơn

6
Process.WaitForExit () là chặn luồng, do đó đồng bộ. Không phải là điểm của câu trả lời, nhưng tôi nghĩ rằng tôi có thể thêm điều này. Thêm process.EnableRaisingEvents = true và sử dụng sự kiện Exited để không đồng bộ hoàn toàn.
Tom

Có phải là không thể chuyển hướng trực tiếp? Tôi sử dụng tất cả các màu của đầu ra sass?
Ini

14

Được rồi, đối với bất kỳ ai muốn đọc cả Lỗi và Đầu ra, nhưng bị bế tắc với bất kỳ giải pháp nào, được cung cấp trong các câu trả lời khác (như tôi), đây là một giải pháp mà tôi đã xây dựng sau khi đọc giải thích MSDN cho thuộc StandardOutputtính.

Câu trả lời dựa trên mã của 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);
}

Cảm ơn đã thêm điều này. Tôi có thể hỏi bạn đang sử dụng lệnh gì không?
T30

Tôi đang phát triển một ứng dụng trong c # được thiết kế để khởi chạy mysqldump.exe, hiển thị cho người dùng mỗi tin nhắn mà ứng dụng tạo ra, đợi cho nó hoàn thành và sau đó thực hiện thêm một số tác vụ. Tôi không thể hiểu bạn đang nói về loại lệnh nào? Toàn bộ câu hỏi này là về việc khởi chạy một quá trình từ c #.
cubrman

1
nếu bạn sử dụng hai trình xử lý riêng biệt, bạn sẽ không gặp bế tắc
Ovi

cũng trong ví dụ của bạn, bạn đọc quy trình. Tiêu chuẩn chỉ một lần ... ngay sau khi bạn khởi động nó, nhưng người ta sẽ muốn đọc nó liên tục trong khi quá trình đang chạy, phải không?
Ovi



4

bạn có thể sử dụng bộ nhớ dùng chung cho 2 tiến trình để liên lạc qua, kiểm tra MemoryMappedFile

Bạn sẽ chủ yếu tạo một tệp ánh xạ bộ nhớ mmftrong quy trình cha mẹ bằng cách sử dụng câu lệnh "bằng cách sử dụng" sau đó tạo quy trình thứ hai cho đến khi nó chấm dứt và để nó ghi kết quả vào việc mmfsử dụng BinaryWriter, sau đó đọc kết quả từ mmfquy trình cha mẹ, bạn cũng có thể truyền mmftên bằng cách sử dụng đối số dòng lệnh hoặc mã cứng.

đảm bảo khi sử dụng tệp được ánh xạ trong quy trình cha mà bạn thực hiện quy trình con ghi kết quả vào tệp được ánh xạ trước khi tệp được ánh xạ được phát hành trong quy trình cha

Ví dụ: quá trình cha mẹ

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

Quá trình con

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

để sử dụng mẫu này, bạn sẽ cần tạo một giải pháp với 2 dự án bên trong, sau đó bạn lấy kết quả xây dựng của quy trình con từ% childDir% / bin / debug và sao chép nó sang% ParentDirectory% / bin / debug sau đó chạy dự án mẹ

childDirparentDirectory là tên thư mục của các dự án của bạn trên pc chúc may mắn :)


1

Cách khởi chạy một quy trình (chẳng hạn như tệp bat, tập lệnh perl, chương trình giao diện điều khiển) và có đầu ra tiêu chuẩn của nó được hiển thị trên biểu mẫu cửa sổ:

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

Bạn có thể tìm thấy ProcessCallertrên liên kết này: Khởi chạy một quy trình và hiển thị đầu ra tiêu chuẩn của nó


1

Bạn có thể đăng nhập quá trình đầu ra bằng mã dưới đây:

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

Giải pháp hiệu quả với tôi trong win và linux là theo dõi

// 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 };
            }
        }
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.