Como acessar os parâmetros da linha de comando?


153

O tutorial Rust não explica como obter parâmetros da linha de comando. fn main()é mostrado apenas com uma lista de parâmetros vazia em todos os exemplos.

Qual é a maneira correta de acessar os parâmetros da linha de comando main?

Respostas:


168

Você pode acessar os argumentos da linha de comando usando as funções std::env::argsou std::env::args_os. Ambas as funções retornam um iterador sobre os argumentos. O primeiro itera sobre Strings (que são fáceis de trabalhar), mas entra em pânico se um dos argumentos não for um unicode válido. O último repete sobre se OsStringnunca entra em pânico.

Observe que o primeiro elemento do iterador é o nome do próprio programa (essa é uma convenção em todos os principais sistemas operacionais); portanto, o primeiro argumento é realmente o segundo elemento iterado.

Uma maneira fácil de lidar com o resultado de argsé convertê-lo para Vec:

use std::env;

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

Você pode usar toda a caixa de ferramentas do iterador padrão para trabalhar com esses argumentos. Por exemplo, para recuperar apenas o primeiro argumento:

use std::env;

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

Você pode encontrar bibliotecas em crates.io para analisar argumentos da linha de comandos:

  • docopt : você acabou de escrever a mensagem de ajuda e o código de análise é gerado para você.
  • aplauso : você descreve as opções que deseja analisar usando uma API fluente. Mais rápido que o docopt e oferece mais controle.
  • getopts : porta da popular biblioteca C. Nível inferior e ainda mais controle.
  • structopt : construído sobre o aplauso, é ainda mais ergonômico de usar.

2
Também com a ferrugem 0.8 você deve usar apenasprintln(args[0])
Leo Correa

6
Os comentários acima (de @LeoCorrea / @ S4M) se referiam a uma versão antiga da resposta; a versão atual da resposta contém as informações mais atualizadas.
Nickolay

22

O Docopt também está disponível para o Rust, que gera um analisador para você a partir de uma sequência de uso. Como um bônus no Rust, uma macro pode ser usada para gerar automaticamente a estrutura e decodificar com base no tipo:

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

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

E você pode obter os argumentos com:

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

O README e a documentação têm muitos exemplos de trabalho completos.

Disclaimer: Eu sou um dos autores desta biblioteca.



10

Para mim, os getopts sempre pareciam muito baixos e o docopt.rs era muito mágico. Quero algo explícito e direto que ainda ofereça todos os recursos, se eu precisar.

É aqui que o clap-rs é útil.
Parece um pouco com o argparse do Python. Aqui está um exemplo de como ele se parece:

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();

Você pode acessar seus parâmetros assim:

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);

(Copiado da documentação oficial )


1
Eu gosto que o clap-rs permite que você defina sua interface em um arquivo yaml. Além disso, produz instruções de uso com uma aparência muito agradável.
mandril Wooters

Isso me ajudou a configurar rapidamente meu aplicativo CLI. Obrigado!
dimitarvp

4

A partir da versão 0.8 / 0.9, o caminho correto para a função args () seria ::std::os::args, ou seja:

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

Parece que o Rust ainda é bastante volátil no momento, mesmo com IO padrão, portanto, isso pode ficar desatualizado rapidamente.


Obrigado pela atualização! Acho que terei que reconsiderar a resposta aceita após o lançamento do 1.0.
precisa saber é

3

Ferrugem mudou novamente. os::args()foi descontinuado em favor de std::args(). Mas std::args()não é uma matriz, ele retorna um iterador . Você pode iterar sobre os argumentos da linha de comando, mas não pode acessá-los com subscritos.

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

Se você quiser os argumentos da linha de comando como um vetor de strings, isso funcionará agora:

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

Ferrugem - aprenda a abraçar a dor da mudança.


8
Agora você só precisa fazer env::args().collect().
tshepang 23/02

2

o que o @barjak disse funciona para strings, mas se você precisar do argumento como um número (neste caso, um uint), precisará converter assim:

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

Confira também o 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

A partir das versões mais recentes do Rust (Rust> 0,10 / 11), a sintaxe da matriz não funciona. Você precisará usar o método get.

[Editar] A sintaxe da matriz funciona (novamente) à noite. Assim, você pode escolher entre o índice getter ou array.

use std::os;

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

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

Esta é uma declaração obsoleta. Os nightlies Rust mais recentes suportam sintaxe de indexação em Vecs. Eu acho que está lá por mais ou menos um mês. Veja este exemplo .
Vladimir Matveev

1

O Rust evoluiu desde a resposta de Calvin em maio de 2013. Agora, seria possível analisar os argumentos da linha de comando com 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);
}

Apenas para constar: as_slice()não existe mais e &argsdeve ser usado.
Slava Semushin

1

O capítulo "No stdlib" do livro Rust aborda como acessar os parâmetros das linhas de comando (outra maneira).

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

Agora, o exemplo também possui o #![no_std]que eu acho que significa que, normalmente, a biblioteca std teria o verdadeiro ponto de entrada para o seu binário e chamaria uma função global chamada main(). Outra opção é 'desativar o maincalço' com #![no_main]. O que, se não me engano, é dizer ao compilador que você está assumindo o controle total sobre como o programa é iniciado.

#![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
}

Eu não acho que essa seja uma maneira 'boa' de fazer as coisas se tudo o que você quer fazer é ler os argumentos da linha de comando. O std::osmódulo mencionado em outras respostas parece ser uma maneira muito melhor de fazer as coisas. Eu posto esta resposta para fins de conclusão.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.