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?를 어떻게 처리 할 수 ​​있는지 궁금합니다. 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
비동기 사랑 해요! 내가 VB.net에서 (약간의 전사 (轉寫)와 함께)이 코드를 사용 할 수 있었다
리처드 바커

'문자열 출력 = process.StandardOutput.ReadToEnd ();' 출력 라인이 많은 경우 큰 문자열을 생성 할 수 있습니다. 비동기 예제와 Ferruccio의 답변은 출력을 한 줄씩 처리합니다.
앤드류 힐

5
참고 : 첫 번째 (동기식) 접근 방식이 올바르지 않습니다! StandardOutput과 StandardError를 동 기적으로 읽을 수 없습니다! 교착 상태가 발생합니다. 적어도 그들 중 하나는 비동기이어야합니다.
S.Serpooshan

6
Process.WaitForExit ()는 스레드 차단이므로 동기식입니다. 대답의 요점은 아니지만 이것을 추가 할 수 있다고 생각했습니다. process.EnableRaisingEvents = true를 추가하고 Exited 이벤트를 사용하여 완전히 비 동기화하십시오.
Tom

직접 리디렉션 할 수 없습니까? sass 출력의 모든 채색을 사용합니까?
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
두 개의 개별 핸들러를 사용하면 교착 상태가 발생하지 않습니다
Ovi

또한 예제에서 프로세스를 읽습니다. 표준 출력은 한 번만 ... 시작한 직후에 프로세스가 실행되는 동안 계속해서 읽으려고합니다.
Ovi



4

두 프로세스가 공유 메모리를 사용하여 통신하고 체크 아웃 할 수 있습니다. MemoryMappedFile

주로 mmf"using"문을 사용하여 부모 프로세스에서 메모리 매핑 된 파일 을 만든 다음 종료 될 때까지 두 번째 프로세스를 만들고 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그리고 parentDirectoryPC 행운에 프로젝트의 폴더 이름입니다 :)


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

승리와 리눅스에서 나를 위해 일한 해결책은 다음과 같습니다.

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