Làm thế nào để truy cập các tham số dòng lệnh?


153

Các Rust hướng dẫn không giải thích làm thế nào để lấy thông số từ dòng lệnh. fn main()chỉ được hiển thị với một danh sách tham số trống trong tất cả các ví dụ.

Cách chính xác để truy cập các tham số dòng lệnh từ là maingì?

Câu trả lời:


168

Bạn có thể truy cập các đối số dòng lệnh bằng cách sử dụng std::env::argshoặc các std::env::args_oshàm. Cả hai hàm trả về một iterator qua các đối số. Các cựu lặp lại trên Strings (dễ làm việc với) nhưng hoảng loạn nếu một trong các đối số không phải là unicode hợp lệ. Cái sau lặp đi lặp lại trên OsStrings và không bao giờ hoảng loạn.

Lưu ý rằng phần tử đầu tiên của iterator là tên của chính chương trình (đây là quy ước trong tất cả các hệ điều hành chính), vì vậy đối số đầu tiên thực sự là phần tử lặp thứ hai.

Một cách dễ dàng để đối phó với kết quả argslà chuyển đổi nó thành Vec:

use std::env;

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

Bạn có thể sử dụng toàn bộ hộp công cụ iterator tiêu chuẩn để làm việc với các đối số này. Ví dụ: chỉ truy xuất đối số đầu tiên:

use std::env;

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

Bạn có thể tìm thấy các thư viện trên crates.io để phân tích các đối số dòng lệnh:

  • docopt : bạn chỉ cần viết thông báo trợ giúp và mã phân tích được tạo cho bạn.
  • vỗ tay : bạn mô tả các tùy chọn bạn muốn phân tích bằng API thông thạo. Nhanh hơn docopt và cung cấp cho bạn nhiều quyền kiểm soát hơn.
  • getopts : cổng của thư viện C phổ biến. Cấp thấp hơn và thậm chí kiểm soát nhiều hơn.
  • structopt : được xây dựng trên đỉnh của tiếng vỗ tay, nó thậm chí còn tiện dụng hơn để sử dụng.

2
Ngoài ra với rỉ sét 0.8 bạn chỉ nên sử dụngprintln(args[0])
Leo Correa

6
Các ý kiến ​​trên (bởi @LeoCorrea / @ S4M) đề cập đến một phiên bản cũ của câu trả lời; phiên bản hiện tại của câu trả lời chứa thông tin cập nhật nhất.
Nickolay

22

Docopt cũng có sẵn cho Rust, công cụ tạo trình phân tích cú pháp cho bạn từ chuỗi sử dụng. Là một phần thưởng trong Rust, một macro có thể được sử dụng để tự động tạo cấu trúc và thực hiện giải mã dựa trên kiểu:

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

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

Và bạn có thể nhận được các đối số với:

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

README và tài liệu có rất nhiều ví dụ hoạt động đầy đủ.

Tuyên bố miễn trừ trách nhiệm: Tôi là một trong những tác giả của thư viện này.



10

Đối với tôi, getopts luôn cảm thấy quá thấp và docopt.rs là quá nhiều phép thuật. Tôi muốn một cái gì đó rõ ràng và đơn giản mà vẫn cung cấp tất cả các tính năng nếu tôi cần chúng.

Đây là nơi clap-rs có ích.
Nó cảm thấy giống như argparse từ Python. Đây là một ví dụ về cách nó trông như thế nào:

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

Bạn có thể truy cập các tham số của mình như vậy:

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

(Sao chép từ tài liệu chính thức )


1
Tôi thích clap-rs đó cho phép bạn xác định giao diện của mình trong tệp yaml. Ngoài ra, nó tạo ra các tuyên bố sử dụng thực sự đẹp.
Chuck Wooters

Điều này giúp tôi nhanh chóng thiết lập ứng dụng CLI của mình. Cảm ơn bạn!
dimitarvp

4

Kể từ phiên bản 0.8 / 0.9, đường dẫn chính xác đến hàm args () sẽ là ::std::os::args, nghĩa là:

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

Có vẻ như Rust vẫn còn khá biến động ngay cả với IO tiêu chuẩn, do đó, điều này có thể trở nên lỗi thời khá nhanh.


Cảm ơn các cập nhật! Đoán tôi sẽ phải xem xét lại câu trả lời được chấp nhận sau khi 1.0 được phát hành.
đóng cửa

3

Rust lại thay đổi. os::args()được phản đối ủng hộ std::args(). Nhưng std::args()không phải là một mảng, nó trả về một iterator . Bạn có thể lặp lại các đối số dòng lệnh, nhưng không thể truy cập chúng bằng các mục con.

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

Nếu bạn muốn các đối số dòng lệnh là một vectơ của chuỗi, điều này sẽ hoạt động ngay bây giờ:

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

Rust - học cách nắm lấy nỗi đau của sự thay đổi.


8
Bây giờ bạn chỉ cần làm env::args().collect().
tshepang 23/2/2015

2

what @barjak nói hoạt động cho các chuỗi, nhưng nếu bạn cần đối số dưới dạng số (trong trường hợp này là một uint), bạn cần chuyển đổi như thế này:

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

Ngoài ra kiểm tra 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

Kể từ phiên bản Rust mới hơn (Rust> 0.10 / 11), cú pháp mảng sẽ không hoạt động. Bạn sẽ sử dụng phương thức get.

[Chỉnh sửa] Cú pháp mảng hoạt động (một lần nữa) hàng đêm. Vì vậy, bạn có thể chọn giữa chỉ số getter hoặc mảng.

use std::os;

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

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

Đây là tuyên bố lỗi thời. Nightlies Rust mới nhất hỗ trợ cú pháp lập chỉ mục trên Vecs. Tôi đoán nó ở đó trong khoảng một tháng. Xem ví dụ này .
Vladimir Matveev

1

Rust đã phát triển kể từ câu trả lời của Calvin từ tháng 5 năm 2013. Bây giờ người ta sẽ phân tích các đối số dòng lệnh bằng 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);
}

Chỉ để cho bản ghi âm thôi: as_slice() không tồn tại nữa và &argsnên được sử dụng thay thế.
Slava Semushin

1

Chương "Cuốn sách không có stdlib" của Rust bao gồm cách truy cập các tham số dòng lệnh (một cách khác).

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

Bây giờ, ví dụ này cũng có #![no_std]cái mà tôi nghĩ có nghĩa là thông thường, thư viện std sẽ có điểm nhập thực sự cho nhị phân của bạn và gọi một hàm toàn cục được gọi main(). Một tùy chọn khác là 'vô hiệu hóa mainshim' với #![no_main]. Nếu tôi không nhầm thì nói với trình biên dịch rằng bạn đang kiểm soát hoàn toàn chương trình của bạn được bắt đầu như thế nào.

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

Tôi không nghĩ rằng đây là một cách làm 'tốt' nếu tất cả những gì bạn muốn làm là đọc các đối số dòng lệnh. Các std::osmô-đun được đề cập trong các câu trả lời khác dường như là một cách tốt hơn để làm việc. Tôi đăng câu trả lời này vì mục đích hoàn thành.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.