Как получить доступ к параметрам командной строки?

153

В руководстве по Rust не объясняется, как получить параметры из командной строки. fn main()отображается только с пустым списком параметров во всех примерах.

Как правильно получить доступ к параметрам командной строки из main ?

shutefan
источник

Ответы:

168

Вы можете получить доступ к аргументам командной строки с помощью функций std::env::argsили std::env::args_os. Обе функции возвращают итератор для аргументов. Первый итерирует по Strings (с которым легко работать), но паникует, если один из аргументов не является допустимым юникодом. Последний перебираетOsString s и никогда не паникует.

Обратите внимание, что первый элемент итератора - это имя самой программы (это соглашение во всех основных ОС), поэтому первый аргумент на самом деле является вторым итеративным элементом.

Простой способ справиться с результатом 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. Быстрее, чем докопт и дает вам больше контроля.
  • getopts : порт популярной библиотеки C. Нижний уровень и еще больше контроля.
  • structopt : построенный поверх хлопка, он еще более эргономичен в использовании.
barjak
источник
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 и документация содержат множество полных рабочих примеров.

Отказ от ответственности: я один из авторов этой библиотеки.

BurntSushi5
источник
10

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

Это где хлопать-RS пригождается.
Это похоже на argparse из 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);

(Скопировано из официальной документации )

MRE
источник
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]);
}

Похоже, что Rust все еще довольно нестабилен, даже со стандартным вводом-выводом, поэтому он может устареть довольно быстро.

Ник
источник
Спасибо за обновление! Думаю, мне придется пересмотреть принятый ответ после выпуска 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().
Чепанг
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

MRE
источник
1

В более новых версиях Rust (Rust> 0.10 / 11) синтаксис массива работать не будет. Вам придется использовать метод get.

[Редактировать] Синтаксис массива работает (снова) в ночное время. Таким образом, вы можете выбрать между индексом получателя или массива.

use std::os;

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

// Compile
 rustc args.rs && ./args hello-world // returns hello-world
stormpat
источник
Это устаревшее утверждение. Последние ночные игры Rust поддерживают индексирование синтаксиса на Vecs. Я предполагаю, что это там в течение месяца или около этого. Смотрите этот пример .
Владимир Матвеев
1

Rust развился после ответа Кальвина в мае 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

В книге Rust «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Модуль упоминается в других ответах , кажется, гораздо лучший способ делать вещи. Я выкладываю этот ответ ради завершения.

thecoshman
источник