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?

shutefan
fonte

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.
barjak
fonte
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.

BurntSushi5
fonte
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. <[email protected]>")
                      .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 )

senhor
fonte
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.

usuario
fonte
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.

John Nagle
fonte
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")
    }
}
Calvin
fonte
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

senhor
fonte
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
stormpat
fonte
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);
}
Rob Latham
fonte
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.

thecoshman
fonte