Come accedere ai parametri della riga di comando?


153

Il tutorial di Rust non spiega come prendere parametri dalla riga di comando. fn main()viene mostrato solo con un elenco di parametri vuoto in tutti gli esempi.

Qual è il modo corretto di accedere ai parametri della riga di comando main?

Risposte:


168

È possibile accedere agli argomenti della riga di comando utilizzando le funzioni std::env::argso std::env::args_os. Entrambe le funzioni restituiscono un iteratore sugli argomenti. Il primo scorre sopra Strings (che sono facili da lavorare) ma si fa prendere dal panico se uno degli argomenti non è unicode valido. Quest'ultimo scorre su se OsStringnon si fa mai prendere dal panico.

Si noti che il primo elemento dell'iteratore è il nome del programma stesso (questa è una convenzione in tutti i principali sistemi operativi), quindi il primo argomento è in realtà il secondo elemento iterato.

Un modo semplice per gestire il risultato di argsè convertirlo in un Vec:

use std::env;

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

È possibile utilizzare l'intero toolbox iteratore standard per lavorare con questi argomenti. Ad esempio, per recuperare solo il primo argomento:

use std::env;

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

Puoi trovare le librerie su crates.io per analizzare gli argomenti della riga di comando:

  • docopt : basta scrivere il messaggio di aiuto e il codice di analisi viene generato per te.
  • applauso : descrivi le opzioni che vuoi analizzare usando un'API fluente. Più veloce di docopt e ti dà più controllo.
  • getopts : porto della popolare libreria C. Livello inferiore e controllo ancora maggiore.
  • strutturato : costruito sopra l'applauso, è ancora più ergonomico da usare.

2
Anche con la ruggine 0,8 dovresti usare soloprintln(args[0])
Leo Correa,

6
I commenti sopra (di @LeoCorrea / @ S4M) si riferivano a una vecchia versione della risposta; la versione attuale della risposta contiene le informazioni più aggiornate.
Nickolay,

22

Docopt è disponibile anche per Rust, che genera un parser per te da una stringa di utilizzo. Come bonus in Rust, una macro può essere utilizzata per generare automaticamente la struttura ed eseguire la decodifica basata sul tipo:

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

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

E puoi ottenere gli argomenti con:

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

Il file README e la documentazione contengono numerosi esempi di lavoro completi.

Disclaimer: sono uno degli autori di questa biblioteca.



10

Per me, getopts è sempre sembrato di livello troppo basso e docopt.rs era troppo magico. Voglio qualcosa di esplicito e diretto che fornisca comunque tutte le funzionalità se ne ho bisogno.

Questo è dove clap-rs è utile.
Sembra un po 'argparse da Python. Ecco un esempio di come appare:

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

Puoi accedere ai tuoi parametri in questo modo:

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

(Copiato dalla documentazione ufficiale )


1
Mi piace che clap-rs ti permetta di definire la tua interfaccia in un file yaml. Inoltre, produce dichiarazioni d'uso davvero belle.
Chuck Wooters,

Questo mi ha aiutato a configurare rapidamente la mia app CLI. Grazie!
dimitarvp

4

A partire dalla versione 0.8 / 0.9, il percorso corretto per la funzione args () sarebbe ::std::os::args, cioè:

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

Sembra che Rust sia ancora piuttosto volatile in questo momento con persino IO standard, quindi questo potrebbe diventare obsoleto abbastanza rapidamente.


Grazie per l'aggiornamento! Suppongo che dovrò riconsiderare la risposta accettata dopo il rilascio di 1.0.
Shutefan,

3

La ruggine è cambiata di nuovo. os::args()è deprecato a favore di std::args(). Ma std::args()non è un array, restituisce un iteratore . È possibile scorrere gli argomenti della riga di comando, ma non è possibile accedervi con i pedici.

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

Se vuoi gli argomenti della riga di comando come vettore di stringhe, ora funzionerà:

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

Ruggine: impara ad abbracciare il dolore del cambiamento.


8
Ora devi solo fare env::args().collect().
Tshepang,

2

cosa dice @barjak per le stringhe, ma se hai bisogno dell'argomento come numero (in questo caso un uint) devi convertirlo in questo modo:

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

Guarda anche 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 partire dalle versioni Rust più recenti (Rust> 0,10 / 11) la sintassi dell'array non funzionerà. Dovrai utilizzare il metodo get.

[Modifica] La sintassi dell'array funziona (di nuovo) di notte. Quindi puoi scegliere tra l'indice getter o array.

use std::os;

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

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

Questa è un'affermazione obsoleta. Gli ultimi nightly di Rust supportano la sintassi di indicizzazione su Vecs. Immagino sia lì da circa un mese. Vedere questo esempio .
Vladimir Matveev,

1

Rust si è evoluto dalla risposta di Calvin del maggio 2013. Ora si analizzano gli argomenti della riga di comando con 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);
}

Solo per la cronaca: as_slice()non esiste più e &argsdovrebbe essere usato invece.
Slava Semushin,

1

Il capitolo "No stdlib" del libro Rust descrive come accedere ai parametri della riga di comando (in un altro modo).

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

Ora, l'esempio ha anche quello #![no_std]che penso significhi che normalmente, la libreria std avrebbe il vero punto di ingresso per il tuo binario e chiamerebbe una funzione globale chiamata main(). Un'altra opzione è "disabilitare lo mainspessore" con #![no_main]. Il che, se non sbaglio, sta dicendo al compilatore che stai prendendo il pieno controllo su come viene avviato il tuo programma.

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

Non penso che questo sia un modo "buono" di fare le cose se tutto ciò che vuoi fare è leggere gli argomenti della riga di comando. Il std::osmodulo menzionato in altre risposte sembra essere un modo molto migliore di fare le cose. Pubblico questa risposta per completezza.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.