web-dev-qa-db-fra.com

Comment accéder aux paramètres de ligne de commande?

Le didacticiel Rust n'explique pas comment utiliser les paramètres de la ligne de commande. fn main() apparaît uniquement avec une liste de paramètres vide dans tous les exemples.

Quelle est la manière correcte d’accéder aux paramètres de ligne de commande à partir de main?

145
shutefan

Vous pouvez accéder aux arguments de la ligne de commande en utilisant le std::env::args ou std::env::args_os fonctions. Les deux fonctions renvoient un itérateur sur les arguments. La première itère sur Strings (avec laquelle il est facile de travailler) mais panique si l’un des arguments n’est pas unicode valide. Ce dernier itère sur OsStrings et ne panique jamais.

Notez que le premier élément de l'itérateur est le nom du programme lui-même (il s'agit d'une convention dans tous les principaux systèmes d'exploitation), de sorte que le premier argument est en réalité le deuxième élément itéré.

Un moyen simple de gérer le résultat de args consiste à le convertir en un fichier Vec:

use std::env;

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

Vous pouvez utiliser la totalité boîte à outils itérateur standard pour travailler avec ces arguments. Par exemple, pour récupérer uniquement le premier argument:

use std::env;

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

Vous pouvez trouver des bibliothèques sur crates.io pour analyser les arguments en ligne de commande:

  • docopt : il vous suffit d'écrire le message d'aide et le code d'analyse syntaxique est généré pour vous.
  • clap : vous décrivez les options que vous souhaitez analyser à l'aide d'une API fluide. Plus rapide que docopt et vous donne plus de contrôle.
  • getopts : port de la bibliothèque C populaire. Niveau inférieur et encore plus de contrôle.
  • structopt : construit sur le dessus, c'est encore plus ergonomique à utiliser.
160
barjak

Docopt est également disponible pour Rust, qui génère un analyseur syntaxique pour vous à partir d'une chaîne d'utilisation. En bonus dans Rust, une macro peut être utilisée pour générer automatiquement la structure et effectuer un décodage basé sur le type:

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

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

Et vous pouvez obtenir les arguments avec:

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

Les README et documentation ont de nombreux exemples de travail complets.

Disclaimer: Je suis l'un des auteurs de cette bibliothèque.

22
BurntSushi5

Rust a l'argument de l'argument CLI de style getopt- dans caisse getopts .

11
Tobu

Pour moi, getopts se sentait toujours trop bas et docopt.rs était trop magique. Je veux quelque chose de simple et explicite qui fournit toutes les fonctionnalités si j'en ai besoin.

C’est là que clap-rs est pratique.
Cela ressemble un peu à argparse de Python. Voici un exemple de ce à quoi il ressemble:

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

Vous pouvez accéder à vos paramètres comme suit:

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

(Copié à partir du documentation officielle )

8
mre

A partir de la version 0.8/0.9, le chemin correct pour la fonction args () serait ::std::os::args, c'est à dire:

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

Il semble que Rust soit encore assez volatile, même avec les entrées/sorties standard, ce qui risque de devenir obsolète assez rapidement.

3
Nick

La rouille a encore changé. os::args() est déconseillé en faveur de std::args(). Mais std::args() n'est pas un tableau, il retourne un itérateur . Vous pouvez parcourir les arguments de la ligne de commande, mais vous ne pouvez pas y accéder avec des indices.

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

Si vous voulez que les arguments de la ligne de commande soient utilisés comme vecteur de chaînes, cela fonctionnera maintenant:

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

Rust - apprenez à accepter la douleur du changement.

3
John Nagle

ce que @barjak dit fonctionne pour les chaînes, mais si vous avez besoin de l'argument sous forme de nombre (dans ce cas, un uint), vous devez convertir comme ceci:

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
Calvin

Consultez également 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
mre

Le chapitre Le livre Rust, "No stdlib" explique comment accéder aux paramètres de la ligne de commande (autre moyen).

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

Maintenant, l’exemple contient également #![no_std], Ce qui, je pense, signifie que, normalement, la bibliothèque std aurait le véritable point d’entrée pour votre binaire et appellerait une fonction globale appelée main(). Une autre option consiste à "désactiver le main shim" avec #![no_main]. Ce qui, si je ne me trompe pas, dit au compilateur que vous prenez le contrôle total du démarrage de votre programme.

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

Je ne pense pas que ce soit une "bonne" façon de faire les choses si tout ce que vous voulez faire est de lire les arguments en ligne de commande. Le module std::os Mentionné dans d'autres réponses semble être une bien meilleure façon de faire les choses. Je publie cette réponse par souci d'achèvement.

1
thecoshman

À partir de plus récent Rust versions (Rust> 0.10/11), la syntaxe du tableau ne fonctionnera pas. Vous devrez utiliser la méthode get.

[Edit] La syntaxe du tableau fonctionne (encore) la nuit. Vous pouvez donc choisir entre le getter ou l’index de tableau.

use std::os;

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

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

La rouille a évolué depuis la réponse de Calvin à compter de mai 2013. On analysera maintenant les arguments en ligne de commande avec 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);
}
1
Rob Latham