본문 바로가기

Rust

러스트로 리눅스 wc명령어 구현

wc명령어는 줄,단어, 바이트 수를 알려주는 기본 명령어이다

 
 
1. 파생패턴을 이용해 명령줄 인수를 파싱을 한다. 

// 파생패턴 
#[derive(Debug,Parser)]
#[command(author,version,about)]
struct Args{
    #[arg(value_name="FILE", default_value="-")]
    // 연속된 문자열은 연속된 배열인 벡터에 담긴다
    files:Vec<String>,

    // 줄수 
    #[arg(short,long)]
    lines:bool,

    // 단어 수 
    #[arg(short,long)]
    words:bool,
    
    // 바이트 수 
    #[arg(short('c'),long)]
    bytes:bool,
    
    // 문자 수 
    #[arg(short('m'),long, conflicts_with("bytes"))]
    chars:bool,   
}

 
 
2. 파싱한 명령줄인수를 받아서 iter.all함수로 파일의 bool값을 테스트를 한다. 필드가 모두다 true라서 true반환, 그리고 wc에 명령에 대한 결과값을 출력한다.

// `iter.all()`을 이용해 값들이 모두 false인지 테스트하는 함수
// `args` 구조체의 필드 중 줄 수(`lines`), 단어 수(`words`), 바이트 수(`bytes`)가 모두 true인지 확인한다.
fn run(mut args: Args) -> Result<()> {
    // `args`의 `lines`, `words`, `bytes`, `chars` 필드가 모두 true 인지 확인.
    if [args.lines, args.words, args.bytes, args.chars].iter()
        . all(|v| v == &false)
    {
        // 모든 필드가 true라 true
        args.lines = true;
        args.words = true;
        args.bytes = true;
    } 

    // 총합을 계산하기 위한 변수들 초기화
    let mut total_lines = 0;
    let mut total_words = 0;
    let mut total_bytes = 0;
    let mut total_chars = 0; 
    
    // 파일 목록에 대해 반복하며 총합 계산
    for filename in &args.files {
        match open(filename) {
            Err(err) => eprintln!("{filename}:{err}"),  // 파일 열기 오류 처리
            Ok(file) => {
                let info = count(file)?;  // 파일의 줄 수, 단어 수, 바이트 수, 문자 수를 계산               
                // 결과를 형식화하여 출력
                println!(
                    "{}{}{}{}{}",
                    format_field(info.num_lines, args.lines),
                    format_field(info.num_words, args.words),
                    format_field(info.num_bytes, args.bytes),
                    format_field(info.num_chars, args.chars),
                    if filename.as_str() == "-" {
                        "".to_string()
                    } else {
                        format!(" {}", filename)
                    }
                );

                // 총합 계산
                total_lines += info.num_lines;
                total_words += info.num_words;
                total_bytes += info.num_bytes;
                total_chars += info.num_chars;
            }  
        }
    }

    // 파일이 여러 개일 경우, 총합 결과를 출력
    if args.files.len() > 1 {
        println!(
            "{}{}{}{} total",
            format_field(total_lines, args.lines),
            format_field(total_words, args.words),
            format_field(total_bytes, args.bytes),
            format_field(total_chars, args.chars),
        )
    }
    Ok(())
}

 
2-1. 파일을 읽기위해 무한 loop를 사용한다.

fn count(mut file: impl BufRead) -> Result<FileInfo> {
    let mut num_lines = 0;
    let mut num_words = 0;
    let mut num_bytes = 0;
    let mut num_chars = 0;
    let mut line = String::new();

    loop {
        //read_line함수를 통해  파일 라인 한줄씩 읽고 바이트 수 반환
        let line_bytes = file.read_line(&mut line)?;
        // 0바이트이면 끝에 닿은것이므로 종료
        if line_bytes == 0 {
            break;
        }
        num_bytes += line_bytes;
        num_lines += 1;
        // 공백을 제외시키고 단어수를 센다
        num_words += line.split_whitespace().count();
        num_chars += line.chars().count();
        // line 버퍼를 비운다 
        line.clear();
    }

    Ok(FileInfo {
        num_lines,
        num_words,
        num_bytes,
        num_chars,
    })
}



 
3. 명령창에서 입력이 "-"가 포함된 명령을 읽고 그렇지 않을 때는 Buffreader로 파일을 열어 한줄씩 읽어서 카운트 함수로 라인 수,단어 수,바이트 수를 계산한다


// 스마트 포인터인 Box를 넣어 힙 메모리에 저장
fn open(filename:&str) -> Result<Box<dyn BufRead>> {
    match  filename {
        "-" => Ok(Box::new(BufReader::new(io::stdin()))),
        _ => Ok(Box::new(BufReader::new(File::open(filename)?)))
    }
}

 
 
 
4. 결과값
 
 
4-1. 각 열의 줄,단어,바이트수를 보여주고 총합계도 출력해준다.

 
 
4-2. 문자 수도 출력해준다.

 
 
 
 
 
 
 
 
참고한 책입니다
https://www.yes24.com/Product/Goods/128929260

커맨드라인 러스트 - 예스24

14가지 유닉스 유틸리티를 만들며 러스트 내 것으로 만들기 개발자들에게 사랑받는 러스트는 복잡한 언어이자 학습곡선이 가파르기로 악명이 높기도 하다. 이 책은 언어 전체에 초점을 맞추기

www.yes24.com