rust的clap如何传参数?

use std::fs::{File, OpenOptions};
use std::io::{BufRead, BufReader, Seek, SeekFrom, Write};
use std::path::Path;
use std::thread;
use std::time::Duration;
use clap::{Command, arg};

fn main() {

    let mut log_file_path_str: &str = "";
    let mut index_file_path_str: &str = "";

    let matches = Command::new("watch_log")
        .version("1.0")
        .author("357888473@qq.com")
        .about("watch log and handle log every line")
        .subcommand_required(true)
        .arg_required_else_help(true)
        .subcommand(
            Command::new("logpath")
                .about("log's path")
                .arg(arg!(--logpath <VALUE>).help("String").required(true)),
        )
        .subcommand(
            Command::new("indexpath")
                .about("index's path")
                .arg(arg!(--indexpath <VALUE>).help("save read log's line count").required(true)),
        ).get_matches();

    match matches.subcommand() {
        Some(("logpath", sub_matches)) => {
            if let Some(input) = sub_matches.get_one::<String>("logpath") {
                log_file_path_str = input;
            }
        },
        Some(("indexpath", sub_matches)) => {
            if let Some(input) = sub_matches.get_one::<String>("indexpath") {
                index_file_path_str = input;
            }
        },
        _ => unreachable!(),
    }


    let log_file_path = Path::new(log_file_path_str); // 日志文件路径
    let index_file_path = Path::new(index_file_path_str); // 索引文件路径

    loop {
        let mut current_line_number = get_current_line_number(&index_file_path); // 获取上次读取的行数
        let mut file = File::open(&log_file_path).unwrap(); // 打开日志文件

        file.seek(SeekFrom::Start(0)).unwrap(); // 移动到文件开头
        let reader = BufReader::new(&mut file);

        let mut new_lines = false; // 用于标记是否有新的日志行读取

        for line in reader.lines().skip(current_line_number as usize) {
            let line = line.unwrap();
            println!("{}", line);

            current_line_number += 1; // 更新当前行数
            new_lines = true;
        }

        if new_lines {
            set_current_line_number(&index_file_path, current_line_number); // 更新索引文件中的行数
        }

        println!("等待新日志...");
        thread::sleep(Duration::from_secs(1)); // 暂停1秒钟,然后重新开始循环
    }
}

fn get_current_line_number(index_file_path: &Path) -> u64 {
    if let Ok(index_content) = std::fs::read_to_string(index_file_path) {
        // 读取索引文件中的内容,转换为已读行数
        if let Ok(current_line_number) = index_content.trim().parse::<u64>() {
            return current_line_number;
        }
    }
    0 // 默认为0
}

fn set_current_line_number(index_file_path: &Path, line_number: u64) {
    // 更新索引文件中的已读行数
    let mut index_file = OpenOptions::new().write(true).create(true).truncate(true).open(index_file_path).unwrap();
    writeln!(index_file, "{}", line_number).expect("无法写入索引文件");
}

请教给为,我用rust 通过命令行传入两个参数 logpath 和 indexpath,但是在执行的时候从事报错,我的执行命令是:
cargo run -- logpath=hello.log --indexpath=index.txt

报错是 error: unexpected argument '--logpath' found
我试过各种命令哈,再比如:
cargo run -- --logpath=hello.log --indexpath=index.txt

我查了很多资料,都无法解决,请各位大佬看下我上边代码,看看是否是我哪里有问题造成的,应该如何传参,谢谢

阅读 774
avatarAI BotBETA

你的代码看起来是正确的,但是在使用 cargo run 命令来运行 Rust 程序时,你需要知道 cargo run 命令实际上会先构建你的项目,然后运行构建出的可执行文件。因此,传递给程序的参数实际上需要传递给这个可执行文件,而不是 cargo run 命令。

在你的情况下,你可能需要两步来运行你的程序。首先,你需要构建你的项目:

cargo build

然后,你可以直接运行构建出的可执行文件,并传递参数给它。在你的例子中,可执行文件的名字可能是 watch_log(这取决于你的 Cargo.toml 文件中 [package] 部分的 name 字段),因此你可以这样运行它:

./target/debug/watch_log logpath=hello.log indexpath=index.txt

或者,如果你正在使用 cargo 的默认配置(即,你的项目在 src/main.rs 文件中),那么构建出的可执行文件的名字应该是 target/debug/your_project_name,其中 your_project_name 是你在 Cargo.toml 文件的 [package] 部分定义的 name。所以,你可能需要这样运行它:

./target/debug/your_project_name logpath=hello.log indexpath=index.txt

注意,在这些命令中,我没有包含 -- 在参数前面,因为 clap 库会自动处理这种情况。--logpath--indexpath 是被 clap 定义为长选项的参数,所以你可以直接传递它们,而不需要 -- 前缀。

希望这能解决你的问题!如果你还有其他问题,欢迎继续提问。

1 个回答

我猜你可能没有理解什么是子命令和选项参数

./abc xxx --dir="/tmp"

其中 xxx 是子命令(sub command),而 --dir 是选项参数(option)

所以如果是根据你的代码你应该运行的命令是

cargo run -- logpath --logpath="hello.log"

输出大概是:

cargo run -- logpath --logpath="/tmp/abc.log"
    Finished dev [unoptimized + debuginfo] target(s) in 0.04s
     Running `target/debug/let-code logpath --logpath=/tmp/abc.log`
123

因为你代码里面写了将 logpath 作为一个子命令,并且接收一个 --logpath 的选项。这挺奇怪的,所以我猜测你没理解。并且这样 --indexpath 是没有办法同时输入的。

我会认为你应该创建一个子命令 monitor 然后将 --logpth--indexpath 作为选项才对。当然由于不知道你的具体需求,所以没法给出更多建议。


我想你需要的是不是这样的实现:

use clap::{arg, Command};

fn main() {
    let mut log_file_path_str: &str = "";
    let mut index_file_path_str: &str = "";

    let matches = Command::new("watch_log")
        .version("1.0")
        .author("357888473@qq.com")
        .about("watch log and handle log every line")
        .subcommand_required(true)
        .arg_required_else_help(true)
        .subcommand(Command::new("monitor").about("log's path").args([
            arg!(--logpath <VALUE>).help("String").required(true),
            arg!(--indexpath <VALUE>).help("String").required(true),
        ]))
        .get_matches();

    match matches.subcommand() {
        Some(("monitor", sub_matches)) => {
            if let Some(input) = sub_matches.get_one::<String>("logpath") {
                log_file_path_str = input;
            }
            if let Some(input) = sub_matches.get_one::<String>("indexpath") {
                index_file_path_str = input;
            }
        }
        _ => unreachable!(),
    }
    println!("{}, {}", log_file_path_str, index_file_path_str);
}
$ cargo run -- monitor --logpath="/tmp/abc.log" --indexpath="/tmp/ttt.log"

/tmp/abc.log, /tmp/ttt.log
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进