Search
Duplicate

C#/ CMD 띄우고 output event 받기

가끔 C# 프로그램 내에서 처리 안 되서 외부 프로그램을 띄워서 처리하고, 그 결과를 받아오는 식으로 구현을 해야 하는 경우가 있다.
개인적으로 그런 예는 보통 python으로 된 코드를 실행하고 그 결과를 받아오는 경우였는데 —물론 C#에서 python을 연동하는 방법도 있지만 한계가 좀 있다—, 이 경우 사용 환경이 Windows라면 아예 CMD를 띄워서 python을 실행하고 그 결과를 받아오는 방법이 가장 확실하다.
CMD를 띄워서 python을 실행하는 것은 사용자가 직접 CMD를 띄워서 python 실행 커맨드를 입력하는 것과 본질적으로 완전히 동일하다.
이렇게 띄워진 CMD는 python 프로세스가 완전히 종료될 때까지 기다리게 되는데, 만일 python 코드 상에서 중간 결과를 output으로 써 준다면 그것을 받아와서 처리해 줄 수 있다.
// commands에는 실제로 CMD를 띄운 후에 사용자가 입력할 command들이 담겨 있다. // ex) python train.py --param 1 --param 2 ... public void StartProcess(IEnumerable<string> commands) { ProcessStartInfo psi = new ProcessStartInfo(); psi.FileName = "cmd.exe"; psi.RedirectStandardInput = true; // input을 쓴다 psi.RedirectStandardOutput = true; // output을 쓴다 psi.RedirectStandardError = true; // pyton tqdm 메시지가 error에 찍힘 psi.UseShellExecute = false; // output을 true로 하려면 shell execute가 false여야 함 psi.CreateNoWindow = true; // 창을 새로 띄우지 않음 using (Process process = Process.Start(psi)) { process.OutputDataReceived += OutputDataReceived; // output 이벤트 구독 process.ErrorDataReceived += ErrorDataReceived; // error 이벤트 구독 process.BeginOutputReadLine(); // output 시작 process.BeginErrorReadLine(); // error 시작 // commad 입력을 위해 stream을 가져온다. // StandardInput을 종료해야 EndOfStream을 처리할 수 있기 때문에 using으로 묶는다 using (StreamWriter sw = process.StandardInput) { if (sw.BaseStream.CanWrite) { foreach (string command in commands) { sw.WriteLine(command); } } } process.WaitForExit(); } }
C#
복사
위 코드에도 나오지만 StandarInput을 종료하지 않으면 Process가 종료된 것을 모르고 무한 대기 하므로 StandardInput은 종료해 주어야 한다.
또한 Error 이벤트는 정말로 Error만 잡히지 않는다는 것에 주의. StandardOutput에 찍히지 못하는 style이 적용된 Output도 Error에 찍힐 수 있다. —예컨대 python에서 console에 progress style을 찍는 경우에 Output이 아니라 Error에 찍힐 수 있다.
위에서 추가한 이벤트는 아래와 같이 처리할 수 있다.
private void OutputDataReceived(object sender, DataReceivedEventArgs e) { // output이 있는지 체크 if (!string.IsNullOrWhiteSpace(e.Data)) { // output을 원하는 pattern으로 parsing 한다. if (Regex.IsMatch(e.Data, "pattern")) { // 처리 로직 } } } private void ErrorDataReceived(object sender, DataReceivedEventArgs e) { if (!string.IsNullOrWhiteSpace(e.Data)) { if (Regex.IsMatch(e.Data, "pattern")) { // 처리 로직 } } }
C#
복사