¿Cómo acceder a los parámetros de la línea de comandos?

153

El tutorial de Rust no explica cómo tomar parámetros de la línea de comando. fn main()solo se muestra con una lista de parámetros vacía en todos los ejemplos.

¿Cuál es la forma correcta de acceder a los parámetros de la línea de comandos main?

shutefan
fuente

Respuestas:

168

Puede acceder a los argumentos de la línea de comandos utilizando las funciones std::env::argso std::env::args_os. Ambas funciones devuelven un iterador sobre los argumentos. El primero itera sobre Strings (que es fácil trabajar con él) pero entra en pánico si uno de los argumentos no es válido unicode. Este último itera sobre OsStringsy nunca se asusta.

Tenga en cuenta que el primer elemento del iterador es el nombre del programa en sí (esta es una convención en todos los sistemas operativos principales), por lo que el primer argumento es en realidad el segundo elemento iterado.

Una manera fácil de lidiar con el resultado de argses convertirlo a Vec:

use std::env;

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

Puede usar toda la caja de herramientas de iterador estándar para trabajar con estos argumentos. Por ejemplo, para recuperar solo el primer argumento:

use std::env;

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

Puede encontrar bibliotecas en crates.io para analizar los argumentos de la línea de comandos:

  • docopt : solo escribe el mensaje de ayuda y se genera el código de análisis.
  • clap : describe las opciones que desea analizar utilizando una API fluida. Más rápido que docopt y te da más control.
  • getopts : puerto de la popular biblioteca C. Nivel inferior y aún más control.
  • structopt : construido sobre el aplauso, es aún más ergonómico de usar.
barjak
fuente
2
También con óxido 0.8 deberías usar soloprintln(args[0])
Leo Correa
66
Los comentarios anteriores (por @LeoCorrea / @ S4M) se referían a una versión anterior de la respuesta; La versión actual de la respuesta contiene la información más actualizada.
Nickolay
22

Docopt también está disponible para Rust, que genera un analizador para usted a partir de una cadena de uso. Como beneficio adicional en Rust, se puede usar una macro para generar automáticamente la estructura y hacer decodificación basada en tipo:

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

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

Y puedes obtener los argumentos con:

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

El archivo README y la documentación tienen muchos ejemplos de trabajo completos.

Descargo de responsabilidad: soy uno de los autores de esta biblioteca.

BurntSushi5
fuente
10

Para mí, los getopts siempre se sentían demasiado bajos y docopt.rs era demasiada magia. Quiero algo explícito y directo que todavía proporcione todas las características si las necesito.

Aquí es donde los aplausos son útiles.
Se siente un poco como argparse de Python. Aquí hay un ejemplo de cómo se ve:

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

Puede acceder a sus parámetros así:

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 de la documentación oficial )

mre
fuente
1
Me gusta que clap-rs te permite definir tu interfaz en un archivo yaml. Además, produce declaraciones de uso realmente bonitas.
Chuck Wooters
Esto me ayudó a configurar rápidamente mi aplicación CLI. ¡Gracias!
dimitarvp
4

A partir de la versión 0.8 / 0.9, la ruta correcta a la función args () sería ::std::os::args, es decir:

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

Parece que Rust todavía es bastante volátil en este momento, incluso con E / S estándar, por lo que puede desactualizarse con bastante rapidez.

Mella
fuente
¡Gracias por la actualización! Supongo que tendré que reconsiderar la respuesta aceptada después de que se lance 1.0.
shutefan
3

El óxido cambió de nuevo. os::args()está en desuso a favor de std::args(). Pero std::args()no es una matriz, devuelve un iterador . Puede iterar sobre los argumentos de la línea de comandos, pero no puede acceder a ellos con subíndices.

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

Si desea los argumentos de la línea de comando como un vector de cadenas, esto funcionará ahora:

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

Rust: aprende a abrazar el dolor del cambio.

John Nagle
fuente
8
Ahora solo necesitas hacer env::args().collect().
tshepang
2

lo que @barjak dijo funciona para cadenas, pero si necesita el argumento como un número (en este caso, un uint), debe convertirlo así:

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
fuente
2

También echa un vistazo a 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
fuente
1

A partir de las versiones más recientes de Rust (Rust> 0.10 / 11), la sintaxis de la matriz no funcionará. Tendrá que usar el método get.

[Editar] La sintaxis de la matriz funciona (de nuevo) en la noche. Por lo tanto, puede elegir entre el getter o el índice de matriz.

use std::os;

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

// Compile
 rustc args.rs && ./args hello-world // returns hello-world
Stormpat
fuente
Esta es una declaración obsoleta. Los últimos nightlies de Rust admiten la sintaxis de indexación en Vecs. Supongo que está allí durante un mes más o menos. Ver este ejemplo .
Vladimir Matveev
1

Rust ha evolucionado desde la respuesta de Calvin de mayo de 2013. Ahora uno analizaría los argumentos de la línea de comandos 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);
}
Rob Latham
fuente
Solo para el registro: as_slice()ya no existe y &argsdebería usarse en su lugar.
Slava Semushin el
1

El capítulo del libro Rust "No stdlib" cubre cómo acceder a los parámetros de las líneas de comando (otra forma).

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

Ahora, el ejemplo también tiene #![no_std]lo que creo que significa que normalmente, la biblioteca estándar tendría el verdadero punto de entrada para su binario y llamaría a una función global llamada main(). Otra opción es 'deshabilitar la maincuña' con #![no_main]. Lo que si no me equivoco es decirle al compilador que está tomando el control total sobre cómo se inicia su programa.

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

No creo que esta sea una 'buena' forma de hacer las cosas si todo lo que quieres hacer es leer los argumentos de la línea de comandos. El std::osmódulo mencionado en otras respuestas parece ser una forma mucho mejor de hacer las cosas. Publico esta respuesta para completarla.

thecoshman
fuente