본문 바로가기

프로그래밍/RUST

[RUST] 인자 입력 받아 명령 프로세스 실행하기

rust 에서 std::process::Command 를 사용해 cmd 명령이나 파일을 실행 할 수 있는데, 어떤 객체들이 사용되는지 대략적으로 살펴본다.

 

use

use std::error::Error as StdError;
use std::fmt;
use std::io::Read;
use std::os::windows::process::CommandExt; // creation flag
use std::process::{self, Command, Stdio, ExitStatus};
use std::env;
use std::path::Path;

 

호출 결과 구조체

struct ProcessResult {
    stdout: Vec<u8>,
    stderr: Vec<u8>,
    exit_status: ExitStatus,
}

 

에러처리

io 에러에 대해서만 처리해 준다.

#[derive(Debug)]
enum Error{
    Io(std::io::Error),
}

impl From<std::io::Error> for Error {
    fn from(err: std::io::Error) -> Self {
        Error::Io(err)
    }
}

impl StdError for Error {
    fn source(&self) -> Option<&(dyn StdError + 'static)> {
        match self {
            Self::Io(err) => Some(err),
        }
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::Io(err) => write!(f, "io error: {}", err),
        }
    }
}

 

명령 호출 함수

프로그램명과 arguments를 입력받아 명령을 호출한다. 표춘출력과 표준에러, 파라미터 목록 전달 후 spawn() 을 호출해 준다. 실행할 프로그램이 없는 경우와 같은 io 에러의 경우 spawn() 에서 발생하며, '?' 를 통해 에러를 그대로 리턴하게 된다.
표준에러의 경우 정상 수행은 되었지만 프로그램의 에러(파라미터 부족 등)의 경우이므로, 정상적인 ProcessResult로 리턴하게 된다.

fn run_process(program: String, args: &Vec<String>) -> Result<ProcessResult, Error> {
    let mut child = Command::new(program)
        .stdout(Stdio::piped())
        .stderr(Stdio::piped())
        .args(args)
        .spawn()?;

    let mut stdout = vec![];
    let child_stdout = child.stdout.take();
    std::io::copy(&mut child_stdout.unwrap(), &mut stdout)?;
    let exit_status = child.wait()?;

    let mut stderr = vec![];
    if let Some(mut reader) = child.stderr {
        reader.read_to_end(&mut stderr)?;
    }
    
    Ok(ProcessResult {
        stdout,
        stderr,
        exit_status,
    })

}

 

프로세스 실행 및 결과 출력

env::args() 로 프로그램 실행시 인자 목록을 가져올 수 있는데, collect() 를 통해 Vec<String> 으로 변환한다.
각 인자 목록 중 0번 째는 현재 실행하는 파일이므로, 1 번째를 실행할 프로그램, 2번 부터 파라미터로 구분해 저장한다.

fn main() {
    println!("Hello, world!");

    let args: Vec<String> = env::args().collect();

    let program = args[1].clone();
    let params: Vec<String> = args[2..].to_vec();


	// 프로세스 실행
    // io 에러발생 시 에러 출력 후 종료, 종료 시 exit code 1
    let result = run_process(program, &params).unwrap_or_else(|err| {
        println!("io error: {:?}", err);
        process::exit(1);
    });

	// 정상 종료
    // exit code 에 따라 표준출력이나 표준에러 확인
    if result.exit_status.success() {
        let stdout = std::str::from_utf8(&result.stdout).expect("invalid output");
        println!("result: {}", stdout);

    } else {
        let stderr = String::from_utf8(result.stderr).unwrap_or_default();
        println!("error: {}\n{}",result.exit_status, stderr);
    }
}

 

테스트

cargo build
.\target\debug\sample.exe java --version