Як отримати доступ до параметрів командного рядка?


153

Підручник « Іржа» не пояснює, як приймати параметри з командного рядка. fn main()відображається лише з порожнім списком параметрів у всіх прикладах.

Який правильний спосіб доступу до параметрів командного рядка main?

Відповіді:


168

Ви можете отримати доступ до аргументів командного рядка за допомогою функцій std::env::argsабо std::env::args_os. Обидві функції повертають ітератор над аргументами. Колишній ітератор над Strings (з яким легко працювати), але панікує, якщо один з аргументів не є дійсним unicode. Останнє повторює OsStringі не панікує.

Зауважте, що перший елемент ітератора - це назва самої програми (це умова у всіх основних ОС), тому перший аргумент - це власне другий ітераційний елемент.

Найпростіший спосіб вирішити результат args- перетворити його на Vec:

use std::env;

fn main() {
    let args: Vec<_> = env::args().collect();
    if args.len() > 1 {
        println!("The first argument is {}", args[1]);
    }
}

Для роботи з цими аргументами можна використовувати весь стандартний інструментальний інструмент ітератора . Наприклад, для отримання лише першого аргументу:

use std::env;

fn main() {
    if let Some(arg1) = env::args().nth(1) {
        println!("The first argument is {}", arg1);
    }
}

Ви можете знайти бібліотеки на crates.io для аналізу аргументів командного рядка:

  • docopt : ви просто пишете довідкове повідомлення, і код розбору генерується для вас.
  • clap : ви описуєте параметри, які потрібно розібрати, використовуючи плавний API. Швидше, ніж docopt і дає вам більше контролю.
  • getopts : порт популярної бібліотеки С. Нижній рівень і ще більше контролю.
  • structopt : побудований поверх хлопа, він ще ергономічніший у використанні.

2
Також з іржею 0,8 вам слід скористатися простоprintln(args[0])
Лео Корреа,

6
У коментарях вище (автор @LeoCorrea / @ S4M) згадувався старий варіант відповіді; поточна версія відповіді містить найновішу інформацію.
Миколай

22

Docopt також доступний для Rust, який генерує для вас аналізатор з рядка використання. Як бонус у Rust, макрос можна використовувати для автоматичного генерування структури та декодування на основі типу:

docopt!(Args, "
Usage: cp [-a] SOURCE DEST
       cp [-a] SOURCE... DIR

Options:
    -a, --archive  Copy everything.
")

А ви можете отримати аргументи за допомогою:

let args: Args = Args::docopt().decode().unwrap_or_else(|e| e.exit());

У README та документації є безліч повних робочих прикладів.

Відмова: Я один з авторів цієї бібліотеки.



10

Для мене getopts завжди відчував себе занадто низьким рівнем, а docopt.rs - занадто багато магії. Я хочу чогось явного і прямого, що все-таки надає всі функції, якщо вони мені потрібні.

Ось тут вам стане в нагоді clap-rs .
Це трохи схоже на аргументацію з Python. Ось приклад того, як це виглядає:

let matches = App::new("myapp")
                      .version("1.0")
                      .author("Kevin K. <kbknapp@gmail.com>")
                      .about("Does awesome things")
                      .arg(Arg::with_name("CONFIG")
                           .short("c")
                           .long("config")
                           .help("Sets a custom config file")
                           .takes_value(true))
                      .arg(Arg::with_name("INPUT")
                           .help("Sets the input file to use")
                           .required(true)
                           .index(1))
                      .arg(Arg::with_name("debug")
                           .short("d")
                           .multiple(true)
                           .help("Sets the level of debugging information"))
                      .get_matches();

Ви можете отримати доступ до своїх параметрів так:

println!("Using input file: {}", matches.value_of("INPUT").unwrap());

// Gets a value for config if supplied by user, or defaults to "default.conf"
let config = matches.value_of("CONFIG").unwrap_or("default.conf");
println!("Value for config: {}", config);

(Скопійовано з офіційної документації )


1
Мені подобається, що clap-rs дозволяє визначати свій інтерфейс у файлі yaml. Крім того, він створює дійсно приємні вигляд заяви про використання.
Чак Вутер

Це допомогло мені швидко налаштувати додаток CLI. Дякую!
dimitarvp

4

Відповідно до версії 0.8 / 0.9, правильним шляхом до функції args () буде ::std::os::args, тобто:

fn main() {
  let args: ~[~str] = ::std::os::args();
  println(args[0]);
}

Здається, що Руст все ще досить мінливий з рівним стандартним IO, тому це може застаріти досить швидко.


Дякуємо за оновлення! Гадаю, мені доведеться переглянути прийняту відповідь після виходу 1.0.
shutefan

3

Іржа знову змінилася. os::args()застаріло на користь std::args(). Але std::args()це не масив, він повертає ітератор . Ви можете повторити аргументи командного рядка, але не можете отримати доступ до них з підписками.

http://doc.rust-lang.org/std/env/fn.args.html

Якщо ви хочете, щоб аргументи командного рядка були як вектор рядків, це працюватиме зараз:

use std::env;
...
let args: Vec<String> = env::args().map(|s| s.into_string().unwrap()).collect();

Іржа - навчіться охоплювати біль змін.


8
Тепер вам потрібно лише робити env::args().collect().
thepang

2

те, що сказав @barjak, працює для рядків, але якщо вам потрібен аргумент у вигляді числа (в даному випадку - uint), вам потрібно перетворити так:

fn main() {
    let arg : ~[~str] = os::args();
    match uint::from_str(arg[1]){
         Some(x)=>io::println(fmt!("%u",someFunction(x))),
         None=>io::println("I need a real number")
    }
}

2

Також перевірте structopt:

extern crate structopt;
#[macro_use]
extern crate structopt_derive;

use structopt::StructOpt;

#[derive(StructOpt, Debug)]
#[structopt(name = "example", about = "An example of StructOpt usage.")]
struct Opt {
    /// A flag, true if used in the command line.
    #[structopt(short = "d", long = "debug", help = "Activate debug mode")]
    debug: bool,

    /// An argument of type float, with a default value.
    #[structopt(short = "s", long = "speed", help = "Set speed", default_value = "42")]
    speed: f64,

    /// Needed parameter, the first on the command line.
    #[structopt(help = "Input file")]
    input: String,

    /// An optional parameter, will be `None` if not present on the
    /// command line.
    #[structopt(help = "Output file, stdout if not present")]
    output: Option<String>,
}

fn main() {
    let opt = Opt::from_args();
    println!("{:?}", opt);
}

https://github.com/TeXitoi/structopt


1

Як і в нових версіях Rust (Rust> 0.10 / 11), синтаксис масиву не працює. Вам доведеться скористатися методом get.

[Редагувати] Синтаксис масиву працює (знову) вночі. Таким чином, ви можете вибрати між getter або індексом масиву.

use std::os;

fn main() {
  let args = os::args();
  println!("{}", args.get(1));
}

// Compile
 rustc args.rs && ./args hello-world // returns hello-world

Це застаріле твердження. Останні сорочки Rust підтримують індексацію синтаксису на Vecs. Я здогадуюсь, що там є місяць або близько того. Дивіться цей приклад .
Володимир Матвєєв

1

Іржа розвивалася з моменту відповіді Келвіна з травня 2013 року. Тепер аргументи командного рядка можна було б розібрати as_slice():

use std::os;

fn seen_arg(x: uint)
{       
    println!("you passed me {}", x);
}
fn main() {
    let args = os::args();
    let args = args.as_slice();
    let nitems = {
            if args.len() == 2 {
                    from_str::<uint>(args[1].as_slice()).unwrap()
            } else {
                    10000
            }
    };

    seen_arg(nitems);
}

Тільки для запису: as_slice()більше не існує, і його &argsслід використовувати замість цього.
Слава Семушин

1

У книзі Іржі "No stdlib" описано, як отримати доступ до параметрів командного рядка (іншим способом).

// Entry point for this program
#[start]
fn start(_argc: isize, _argv: *const *const u8) -> isize {
    0
}

Тепер, у прикладі є також те, #![no_std]що, на мою думку, означає, що зазвичай, std-бібліотека матиме справжню точку входу для вашої бінарної системи і викликає глобальну функцію, яка називається main(). Ще один варіант - "відключити mainпрокладку" за допомогою #![no_main]. Що, якщо я не помиляюся, говорить компілятору, що ви берете повний контроль над тим, як запускається програма.

#![no_std]
#![no_main]

#[no_mangle] // ensure that this symbol is called `main` in the output
pub extern fn main(argc: isize, argv: *const *const u8) -> isize {
    0
}

Я не думаю, що це "хороший" спосіб робити справи, якщо все, що ви хочете зробити, - це прочитати аргументи командного рядка. std::osМодуль згадується в інших відповідях , здається, набагато кращий спосіб робити речі. Цю відповідь публікую заради завершення.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.