Bagaimana cara mengakses parameter baris perintah?

153

The Rust tutorial tidak menjelaskan bagaimana untuk mengambil parameter dari baris perintah. fn main()hanya ditampilkan dengan daftar parameter kosong di semua contoh.

Dari mana cara yang benar mengakses parameter baris perintah main?

shutefan
sumber

Jawaban:

168

Anda dapat mengakses argumen baris perintah dengan menggunakan fungsi std::env::argsatau std::env::args_os. Kedua fungsi mengembalikan iterator atas argumen. Mantan iterates over Strings (yang mudah untuk bekerja dengan) tetapi panik jika salah satu argumen tidak valid unicode. Yang terakhir ini berulang-ulang OsStringdan tidak pernah panik.

Perhatikan bahwa elemen pertama dari iterator adalah nama dari program itu sendiri (ini adalah konvensi di semua OS utama), jadi argumen pertama sebenarnya adalah elemen iterated kedua.

Cara mudah untuk menangani hasilnya argsadalah dengan mengubahnya menjadi Vec:

use std::env;

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

Anda dapat menggunakan seluruh kotak alat iterator standar untuk bekerja dengan argumen ini. Misalnya, untuk mengambil hanya argumen pertama:

use std::env;

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

Anda dapat menemukan pustaka di crates.io untuk parsing argumen baris perintah:

  • docopt : Anda baru saja menulis pesan bantuan, dan kode parsing dibuat untuk Anda.
  • clap : Anda mendeskripsikan opsi yang ingin Anda parsing menggunakan API yang lancar. Lebih cepat dari pada docopt dan memberi Anda lebih banyak kontrol.
  • getopts : port dari perpustakaan C yang populer. Level yang lebih rendah dan bahkan lebih banyak kontrol.
  • structopt : dibangun di atas tepukan, bahkan lebih ergonomis untuk digunakan.
barjak
sumber
2
Juga dengan karat 0,8 Anda harus menggunakan hanyaprintln(args[0])
Leo Correa
6
Komentar di atas (oleh @LeoCorrea / @ S4M) merujuk pada versi lama dari jawabannya; versi terbaru dari jawaban ini berisi informasi terbaru.
Nickolay
22

Docopt juga tersedia untuk Rust, yang menghasilkan parser untuk Anda dari string penggunaan. Sebagai bonus di Rust, makro dapat digunakan untuk secara otomatis menghasilkan struct dan melakukan decoding berdasarkan tipe:

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

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

Dan Anda bisa mendapatkan argumen dengan:

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

README dan dokumentasi memiliki banyak contoh yang berfungsi penuh.

Penafian: Saya adalah salah satu penulis perpustakaan ini.

BurntSushi5
sumber
10

Bagi saya, getop selalu terasa terlalu rendah dan docopt.rs terlalu ajaib. Saya menginginkan sesuatu yang eksplisit dan langsung yang masih menyediakan semua fitur jika saya membutuhkannya.

Di sinilah clap-rs berguna.
Rasanya agak seperti argparse dari Python. Berikut ini contoh tampilannya:

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

Anda dapat mengakses parameter Anda seperti:

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

(Disalin dari dokumentasi resmi )

mre
sumber
1
Saya suka itu clap-rs memungkinkan Anda mendefinisikan antarmuka Anda dalam file yaml. Juga, ini menghasilkan pernyataan penggunaan yang tampak sangat bagus.
Chuck Wooters
Ini membantu saya dengan cepat mengatur aplikasi CLI saya. Terima kasih!
dimitarvp
4

Pada versi 0.8 / 0.9, jalur yang benar ke fungsi args () adalah ::std::os::args, yaitu:

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

Tampaknya Rust masih cukup fluktuatif sekarang bahkan dengan IO standar, jadi ini mungkin menjadi usang cukup cepat.

Nick
sumber
Terima kasih atas pembaruannya! Kira saya harus mempertimbangkan kembali jawaban yang diterima setelah 1.0 dirilis.
shutefan
3

Rust berubah lagi. os::args()tidak digunakan lagi std::args(). Tetapi std::args()bukan sebuah array, ia mengembalikan iterator . Anda dapat mengulangi argumen baris perintah, tetapi tidak dapat mengaksesnya dengan subskrip.

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

Jika Anda ingin argumen baris perintah sebagai vektor string, ini akan berfungsi sekarang:

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

Karat - belajar merangkul rasa sakit karena perubahan.

John Nagle
sumber
8
Anda sekarang hanya perlu melakukannya env::args().collect().
tshepang
2

apa yang dikatakan @barjak berfungsi untuk string, tetapi jika Anda memerlukan argumen sebagai angka (dalam hal ini uint), Anda perlu mengonversi seperti ini:

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

Lihat juga 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
sumber
1

Pada versi Rust yang lebih baru (Rust> 0.10 / 11) sintaks array tidak berfungsi. Anda harus menggunakan metode get.

[Sunting] Sintaks array berfungsi (lagi) di malam hari. Jadi, Anda dapat memilih antara indeks pengambil atau 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
sumber
Ini adalah pernyataan usang. Nightly Rust terbaru mendukung sintaks pengindeksan pada Vecs. Saya kira itu ada di sana selama sebulan atau lebih. Lihat contoh ini .
Vladimir Matveev
1

Rust telah berevolusi sejak jawaban Calvin dari Mei 2013. Sekarang orang akan menguraikan argumen baris perintah dengan 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
sumber
Sebagai catatan: as_slice()tidak ada lagi dan &argsharus digunakan sebagai gantinya.
Slava Semushin
1

The book Rust "Tidak ada stdlib" bab penutup cara mengakses parameter baris perintah (cara lain).

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

Sekarang, contoh itu juga memiliki #![no_std]yang saya pikir berarti bahwa biasanya, perpustakaan std akan memiliki titik masuk yang benar untuk biner Anda dan memanggil fungsi global yang disebut main(). Opsi lain adalah 'nonaktifkan mainshim' dengan #![no_main]. Yang jika saya tidak salah mengatakan kepada kompiler bahwa Anda mengambil kendali penuh atas bagaimana program Anda dimulai.

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

Saya tidak berpikir ini adalah cara yang 'baik' dalam melakukan sesuatu jika semua yang ingin Anda lakukan adalah membaca argumen baris perintah. The std::osModul disebutkan dalam jawaban lainnya tampaknya menjadi cara yang lebih baik dalam melakukan sesuatu. Saya mengirim jawaban ini demi penyelesaian.

terima kasih
sumber