Rust命令行工具库nu-cli的使用,nu-cli提供高效终端交互与脚本解析功能

Rust命令行工具库nu-cli的使用,nu-cli提供高效终端交互与脚本解析功能

简介

nu-cli crate实现了Nushell交互式REPL的核心功能,并与reedline集成。目前实现了语法高亮和自动补全逻辑,还包含一些针对reedline的特定命令。

内部Nushell crate

这个crate实现了Nushell的组件,主要供内部使用,不直接支持插件开发或其他外部用户。

安装

在项目目录中运行以下Cargo命令:

cargo add nu-cli

或在Cargo.toml中添加以下行:

nu-cli = "0.106.1"

示例代码

以下是一个使用nu-cli创建简单REPL的示例:

use nu_cli::{
    create_default_context,
    evaluate_script,
    evaluate_repl,
    EvaluationContext
};

fn main() {
    // 创建默认上下文
    let mut context = create_default_context().unwrap();
    
    // 评估脚本
    let script = "echo 'Hello, NuShell!'";
    let result = evaluate_script(&mut context, script);
    println!("Script evaluation result: {:?}", result);
    
    // 启动REPL
    println!("Starting REPL...");
    evaluate_repl(&mut context).unwrap();
}

更完整的示例

下面是一个更完整的示例,展示了如何使用nu-cli创建自定义命令和处理输入:

use nu_cli::{
    create_default_context,
    evaluate_script,
    EvaluationContext,
    CommandRegistry
};
use nu_protocol::{Signature, Value, ShellError};

// 自定义命令
fn my_command(context: &EvaluationContext, args: Vec<Value>) -> Result<Value, ShellError> {
    println!("Custom command executed with args: {:?}", args);
    Ok(Value::nothing())
}

fn main() {
    // 创建默认上下文
    let mut context = create_default_context().unwrap();
    
    // 获取命令注册表
    let mut registry = CommandRegistry::new();
    
    // 注册自定义命令
    registry.register_command(
        "mycmd",  // 命令名
        Signature::build("mycmd"),  // 命令签名
        my_command  // 命令处理函数
    );
    
    // 将注册表添加到上下文中
    context.add_commands(registry);
    
    // 评估使用自定义命令的脚本
    let script = r#"
        echo "Before custom command"
        mycmd arg1 arg2
        echo "After custom command"
    "#;
    
    match evaluate_script(&mut context, script) {
        Ok(_) => println!("Script executed successfully"),
        Err(e) => eprintln!("Error executing script: {:?}", e),
    }
}

完整示例demo

以下是一个更全面的nu-cli使用示例,展示了REPL交互、自定义命令和脚本评估的完整功能:

use nu_cli::{
    create_default_context,
    evaluate_script,
    evaluate_repl,
    EvaluationContext,
    CommandRegistry
};
use nu_protocol::{Signature, Value, ShellError};

// 自定义计算命令
fn calculate(_: &EvaluationContext, args: Vec<Value>) -> Result<Value, ShellError> {
    if args.len() < 3 {
        return Err(ShellError::MissingParameter {
            param_name: "operator and operands".to_string(),
            span: None,
        });
    }

    let op = args[0].as_string()?;
    let x = args[1].as_i64()?;
    let y = args[2].as_i64()?;

    let result = match op.as_str() {
        "+" => x + y,
        "-" => x - y,
        "*" => x * y,
        "/" => x / y,
        _ => return Err(ShellError::UnsupportedOperator(op)),
    };

    Ok(Value::int(result, None))
}

// 自定义问候命令
fn greet(_: &EvaluationContext, args: Vec<Value>) -> Result<Value, ShellError> {
    let name = if !args.is_empty() {
        args[0].as_string()?
    } else {
        "World".to_string()
    };

    println!("Hello, {}!", name);
    Ok(Value::nothing())
}

fn main() {
    // 创建默认上下文
    let mut context = create_default_context().unwrap();
    
    // 创建命令注册表
    let mut registry = CommandRegistry::new();
    
    // 注册自定义命令
    registry.register_command(
        "calc",
        Signature::build("calc")
            .required("operator", SyntaxShape::String)
            .required("x", SyntaxShape::Int)
            .required("y", SyntaxShape::Int),
        calculate
    );
    
    registry.register_command(
        "greet",
        Signature::build("greet").optional("name", SyntaxShape::String),
        greet
    );
    
    // 将命令添加到上下文
    context.add_commands(registry);
    
    // 评估脚本示例
    let script = r#"
        echo "开始计算演示"
        calc + 10 5
        calc * 3 7
        echo "开始问候演示"
        greet
        greet "Nushell"
    "#;
    
    println!("执行脚本:");
    match evaluate_script(&mut context, script) {
        Ok(output) => println!("脚本输出: {:?}", output),
        Err(e) => eprintln!("脚本执行错误: {:?}", e),
    }
    
    // 启动交互式REPL
    println!("启动REPL,输入'exit'退出");
    evaluate_repl(&mut context).unwrap();
}

文档

更多详细信息和API参考请参阅官方文档。

注意

nu-cli主要是Nushell的内部组件,如果您需要构建命令行应用程序,可能需要考虑其他更通用的Rust CLI库如clap或structopt。


1 回复

Rust命令行工具库nu-cli的使用指南

介绍

nu-cli是一个用Rust编写的命令行工具库,提供了高效的终端交互和脚本解析功能。它源自流行的nushell项目,专注于为开发者提供强大的命令行处理能力。

主要特性:

  • 高性能的终端交互
  • 强大的脚本解析引擎
  • 灵活的管道操作
  • 丰富的内置命令
  • 易于扩展的插件系统

安装方法

在Cargo.toml中添加依赖:

[dependencies]
nu-cli = "0.60"  # 请使用最新版本

基本使用方法

1. 创建简单的CLI应用

use nu_cli::{create_default_context, run_command};
use nu_parser::parse;
use nu_protocol::engine::StateWorkingSet;

fn main() {
    // 创建执行上下文
    let mut context = create_default_context().unwrap();
    
    // 解析并执行命令
    let input = "echo 'Hello, nu-cli!'";
    let (block, _) = parse(&mut StateWorkingSet::new(&context.engine_state), input, false);
    
    match run_command(&block, &mut context, None) {
        Ok(_) => println!("Command executed successfully"),
        Err(e) => eprintln!("Error: {}", e),
    }
}

2. 处理管道操作

use nu_cli::{create_default_context, run_command};
use nu_parser::parse;
use nu_protocol::engine::StateWorkingSet;

fn main() {
    let mut context = create_default_context().unwrap();
    
    // 管道示例:获取当前目录文件并筛选出.rs文件
    let input = "ls | where name =~ '..rs$'";
    let (block, _) = parse(&mut StateWorkingSet::new(&context.engine_state), input, false);
    
    run_command(&block, &mut context, None).unwrap();
}

3. 自定义命令

use nu_cli::{create_default_context, run_command};
use nu_engine::eval_block;
use nu_parser::parse;
use nu_protocol::{
    engine::{EngineState, StateWorkingSet},
    Signature, Value,
};
use nu_protocol::{Example, PipelineData, ShellError, Span};

// 定义自定义命令
fn hello_command(args: &[Value], _: &EngineState) -> Result<PipelineData, ShellError> {
    let name = if args.is_empty() {
        "World".to_string()
    } else {
        args[0].as_string()?
    };
    
    Ok(PipelineData::Value(Value::String {
        val: format!("Hello, {}!", name),
        span: Span::unknown(),
    }))
}

fn main() {
    let mut context = create_default_context().unwrap();
    
    // 添加自定义命令到引擎状态
    context.engine_state.add_command(
        "hello".to_string(),
        Signature::build("hello").optional("name", "The name to greet"),
        hello_command,
    );
    
    // 使用自定义命令
    let input = "hello \"nu-cli\"";
    let (block, _) = parse(&mut StateWorkingSet::new(&context.engine_state), input, false);
    
    run_command(&block, &mut context, None).unwrap();
}

高级功能

1. 脚本执行

use nu_cli::{create_default_context, run_script};
use std::path::Path;

fn main() {
    let mut context = create_default_context().unwrap();
    
    // 执行脚本文件
    let script_path = Path::new("example.nu");
    match run_script(&mut context, script_path, None) {
        Ok(_) => println!("Script executed successfully"),
        Err(e) => eprintln!("Error: {}", e),
    }
}

2. 交互式终端

use nu_cli::create_default_context;
use nu_cli::run_repl;

fn main() {
    let mut context = create_default_context().unwrap();
    
    // 启动交互式REPL
    if let Err(e) = run_repl(&mut context) {
        eprintln!("REPL error: {}", e);
    }
}

实际应用示例

文件处理管道

use nu_cli::{create_default_context, run_command};
use nu_parser::parse;
use nu_protocol::engine::StateWorkingSet;

fn main() {
    let mut context = create_default_context().unwrap();
    
    // 复杂管道示例:统计当前目录下.rs文件的行数
    let input = r#"
        ls *.rs 
        | each { |file| 
            open $file.name 
            | lines 
            | length 
            | { name: $file.name, lines: $in } 
        }
        | sort-by lines -r
        | first 5
    "#;
    
    let (block, _) = parse(&mut StateWorkingSet::new(&context.engine_state), input, false);
    run_command(&block, &mut context, None).unwrap();
}

完整示例DEMO

下面是一个结合多个功能的完整示例,展示如何创建一个支持自定义命令、管道操作和脚本执行的CLI应用:

use nu_cli::{create_default_context, run_command, run_script};
use nu_engine::eval_block;
use nu_parser::parse;
use nu_protocol::{
    engine::{EngineState, StateWorkingSet},
    Signature, Value,
};
use nu_protocol::{PipelineData, ShellError, Span};
use std::path::Path;

// 自定义命令:计算平方
fn square_command(args: &[Value], _: &EngineState) -> Result<PipelineData, ShellError> {
    let num = if args.is_empty() {
        0
    } else {
        args[0].as_int()?
    };
    
    Ok(PipelineData::Value(Value::Int {
        val: num * num,
        span: Span::unknown(),
    }))
}

fn main() {
    // 创建上下文
    let mut context = create_default_context().unwrap();
    
    // 添加自定义命令
    context.engine_state.add_command(
        "square".to_string(),
        Signature::build("square").required("number", "The number to square"),
        square_command,
    );
    
    // 示例1: 使用自定义命令
    println!("=== 示例1: 使用自定义命令 ===");
    let input1 = "square 5";
    let (block1, _) = parse(&mut StateWorkingSet::new(&context.engine_state), input1, false);
    run_command(&block1, &mut context, None).unwrap();
    
    // 示例2: 管道操作
    println!("\n=== 示例2: 管道操作 ===");
    let input2 = "1..10 | each { |it| square $it } | math sum";
    let (block2, _) = parse(&mut StateWorkingSet::new(&context.engine_state), input2, false);
    run_command(&block2, &mut context, None).unwrap();
    
    // 示例3: 执行脚本文件
    println!("\n=== 示例3: 执行脚本文件 ===");
    let script_path = Path::new("demo.nu");
    if let Err(e) = run_script(&mut context, script_path, None) {
        eprintln!("没有找到脚本文件 demo.nu,跳过此示例");
    }
    
    // 示例4: 启动交互式REPL
    println!("\n=== 示例4: 启动交互式REPL ===");
    println!("输入 exit 退出REPL");
    if let Err(e) = nu_cli::run_repl(&mut context) {
        eprintln!("REPL error: {}", e);
    }
}

注意事项

  1. nu-cli仍在积极开发中,API可能会有变动
  2. 错误处理应使用Result模式,妥善处理可能的错误
  3. 对于性能敏感的应用,建议复用EngineStateContext对象

nu-cli为Rust开发者提供了强大的命令行处理能力,特别适合需要复杂命令行交互或脚本支持的应用程序开发。

回到顶部