Rust 명령줄 인자
연습 삼아 만들어 볼 만한 작은 프로젝트, 예를 들어 할 일 목록이나 단어 세기 프로그램은 대부분 값을 소스 코드에 박아 두는 대신 명령줄에서 입력을 받아야 해요. 이 페이지에서는 그 기초를 다뤄요.
std::env::args로 인자 읽기
섹션 제목: “std::env::args로 인자 읽기”use std::env;
fn main() { let args: Vec<String> = env::args().collect(); println!("{:?}", args);}이 코드를 greet라는 프로그램으로 컴파일해서 이렇게 실행하면
./greet Alice다음과 같이 나와요.
["greet", "Alice"]첫 번째 요소는 항상 프로그램 자기 이름이에요. 내가 실제로 입력한 인자는 인덱스 1부터 시작해요.
특정 인자 읽기
섹션 제목: “특정 인자 읽기”use std::env;
fn main() { let args: Vec<String> = env::args().collect();
if args.len() < 2 { println!("사용법: greet <이름>"); return; }
let name = &args[1]; println!("안녕하세요, {name}님!");}cargo run -- Alice로 실행하면 이렇게 나와요.
안녕하세요, Alice님!인자 없이 실행하면 프로그램이 죽는 대신 사용법 메시지가 나와요. 인덱스로 접근하기 전에 args.len()을 확인해 두면, 누가 인자를 깜빡하고 안 넘겨도 패닉이 나지 않아요.
인자를 숫자로 바꾸기
섹션 제목: “인자를 숫자로 바꾸기”인자는 전부 String으로 들어오기 때문에, 숫자로 쓰려면 (타입 변환 페이지에서 본) .parse()가 필요할 때가 많아요.
use std::env;
fn main() { let args: Vec<String> = env::args().collect();
if args.len() < 2 { println!("사용법: square <숫자>"); return; }
match args[1].parse::<i32>() { Ok(n) => println!("{}", n * n), Err(_) => println!("올바른 숫자가 아니에요"), }}간단한 플래그 직접 처리하기
섹션 제목: “간단한 플래그 직접 처리하기”플래그(flag)는 --shout처럼 어떤 기능을 켜는 인자예요. 크레이트를 쓰기 전에, 직접 처리하면 어떻게 되는지 먼저 보면 도움이 돼요. 테스트하기 쉽게 만드는 요령은, 인자를 슬라이스 &[String]으로 받는 함수에 로직을 넣는 거예요. 그러면 main은 진짜 인자를 넘기고, 실험할 때는 직접 만든 목록을 넘길 수 있어요.
한글에는 대문자가 없어서, 이 예제의 인사말은 to_uppercase()의 효과가 잘 보이도록 영어로 두었어요.
fn run(args: &[String]) { let mut shout = false; let mut names = Vec::new();
// 인덱스 0(프로그램 이름)은 건너뛰어요 for arg in &args[1..] { if arg == "--shout" { shout = true; } else { names.push(arg); } }
if names.is_empty() { println!("사용법: greet [--shout] <이름>..."); return; }
for name in names { let line = format!("Hello, {name}!"); if shout { println!("{}", line.to_uppercase()); } else { println!("{line}"); } }}
fn main() { // 실제 프로그램에서는 이렇게 써요. // let args: Vec<String> = std::env::args().collect(); // 여기서는 Playground에서도 돌아가도록 목록을 직접 만들어요. let args = vec![ String::from("greet"), String::from("--shout"), String::from("Ana"), String::from("Ben"), ]; run(&args);}실행 결과:
HELLO, ANA!HELLO, BEN!몇 가지 짚어 볼게요.
&args[1..]는 프로그램 이름을 뺀 나머지 인자 전부예요. 플래그는 어디에 와도 괜찮아요.greet Ana --shout Ben도 잘 돌아가요.- 목록에서
--shout를 지우고 다시 실행해 보세요. 평범한 인사가 나와요. 이름까지 지우면 사용법 메시지가 나와요. run은 목록이 어디서 왔는지 신경 쓰지 않으니,std::env::args().collect()로 바꾸는 건main에서 한 줄만 고치면 돼요.
플래그가 한두 개라면 이걸로 충분해요. 하지만 짧은 버전(-s), 플래그 뒤에 오는 값(--count 3), --help 메시지까지 원하게 되면 직접 짠 반복문은 금방 커져요. 그때가 clap으로 넘어갈 때예요.
인자가 몇 개를 넘어가면 clap을 쓰세요
섹션 제목: “인자가 몇 개를 넘어가면 clap을 쓰세요”--verbose 같은 플래그, 있어도 되고 없어도 되는 값, 친절한 --help 출력이 필요해지면 args에 인덱스로 직접 접근하는 방식은 금방 감당하기 어려워져요. 이럴 때 표준처럼 쓰는 해결책이 clap (영어) 크레이트예요. 이렇게 추가해요.
cargo add clap --features deriveuse clap::Parser;
#[derive(Parser)]struct Args { name: String,
#[arg(short, long, default_value_t = 1)] count: u32,}
fn main() { let args = Args::parse(); for _ in 0..args.count { println!("안녕하세요, {}님!", args.name); }}구조체 정의만으로 인자 파싱, 값 검증, 제대로 동작하는 --help 메시지까지 모두 만들어져요. 간단한 스크립트에는 clap이 필요 없지만, 한 번 이상 실제로 쓸 도구라면 손으로 할 일을 많이 덜어 줘요.
연습 문제
섹션 제목: “연습 문제”Playground에서는 진짜 인자를 넘길 수 없어서, 두 문제 모두 env::args().collect()가 돌려주는 것과 똑같이 생긴 Vec<String>을 직접 만들어서 시작해요. 내 컴퓨터에서는 이걸 진짜 인자로 바꾸고 cargo run -- ...로 실행해 보세요.
1. 인자 목록 출력하기
섹션 제목: “1. 인자 목록 출력하기”vec가 주어졌을 때, 사용자가 입력한 인자가 몇 개인지(프로그램 이름은 빼고) 출력하세요. 그다음 각 인자를 1부터 시작하는 번호와 함께 출력하세요.
힌트: 개수는 args.len() - 1이에요. for i in 1..args.len() 반복문을 쓰면 프로그램 이름 다음부터 인덱스를 하나씩 얻을 수 있어요.
정답 보기
fn main() { // std::env::args().collect()에서 왔다고 생각해 주세요 let args = vec![ String::from("notes"), String::from("add"), String::from("우유 사기"), ];
println!("인자를 {}개 넘겼어요", args.len() - 1); for i in 1..args.len() { println!("{i}: {}", args[i]); }}실행 결과:
인자를 2개 넘겼어요1: add2: 우유 사기"우유 사기"는 인자 하나로 쳐요. 실제 명령줄에서는 따옴표로 감싸서 입력하고(cargo run -- add "우유 사기"), 셸이 그걸 문자열 하나로 넘겨 주거든요. 인덱스 0은 여전히 프로그램 이름이라서 반복문이 1부터 시작해요.
2. 숫자 더하기
섹션 제목: “2. 숫자 더하기”vec!["sum", "4", "10", "x", "6"]이 주어졌을 때, 올바른 숫자인 인자는 모두 더하고, 숫자가 아닌 인자에는 경고를 출력하세요. 마지막으로 합계를 출력하세요.
힌트: &args[1..]를 반복하면서 인자를 숫자로 바꾸기에서처럼 match arg.parse::<i32>()를 쓰세요. 반복문 바깥에 let mut total = 0;을 두면 돼요.
정답 보기
fn main() { let args = vec![ String::from("sum"), String::from("4"), String::from("10"), String::from("x"), String::from("6"), ];
let mut total = 0; for arg in &args[1..] { match arg.parse::<i32>() { Ok(n) => total += n, Err(_) => println!("'{arg}'은(는) 숫자가 아니라서 건너뛰어요"), } } println!("합계: {total}");}실행 결과:
'x'은(는) 숫자가 아니라서 건너뛰어요합계: 20인자는 "4"조차도 모두 텍스트로 들어오기 때문에 하나하나 파싱해야 해요. 잘못된 입력을 메시지와 함께 건너뛰는 편이 프로그램이 죽는 것보다 친절해요. 프로그램에 따라서는 첫 번째 잘못된 값에서 return으로 멈추는 게 더 나을 수도 있어요.
std::env::args().collect()는 명령줄 인자를Vec<String>으로 줘요. 인덱스 0은 항상 프로그램 이름이에요.- 인덱스로 접근하기 전에
args.len()을 확인하세요. 그래야 인자가 빠져도 패닉이 나지 않아요. .parse()로String인자를 숫자로 바꿔요. 다른 곳에서 쓰던 것과 똑같아요.- 인자 처리는
&[String]을 받는 함수에 넣으세요. 그러면 직접 만든 목록으로 시험해 볼 수 있어요. - 단순한 인자 몇 개를 넘어서면,
clap크레이트가 많이 쓰이고 검증된 표준적인 선택이에요.
이해도 점검
섹션 제목: “이해도 점검”이해도 점검
1. env::args().collect()를 호출했을 때, 결과로 나온 Vec의 첫 번째 요소는 무엇일까요?
2. args에 인덱스로 접근하기 전에 args.len()을 확인하는 이유는 무엇일까요?
3. args에 직접 인덱스로 접근하는 방식에는 없고 clap 크레이트에는 있는 것은 무엇일까요?
점수: 0 / 3
더 알아보기
섹션 제목: “더 알아보기”- 커맨드 라인 인수 받기 — The Rust Programming Language 한국어판: 명령줄에서 검색어와 파일 이름을 읽어 오는 공식 문서의
minigrep프로젝트가 여기서 시작돼요. std::env::args— 표준 라이브러리 문서 (영어):args()가 정확히 무엇을 돌려주는지, 언제 패닉이 나는지(유니코드가 아닌 인자) 설명해요.- Program arguments — Rust by Example (영어): 짧은 예제와 함께,
match로 인자를 매칭하는 방법 (영어)도 이어서 다뤄요. - Command Line Applications in Rust (영어): 인자 파싱부터 테스트와 배포까지, 실제 CLI 도구를 만드는 방법을 다룬 무료 미니북이에요.
- clap derive tutorial — docs.rs (영어):
clap으로 플래그, 옵션, 서브커맨드, 값 검증을 다루는 단계별 가이드예요.