Rust系统退出码管理库sysexits的使用,提供标准化的Unix/C风格程序退出状态码常量定义

sysexits-rs

CI Version MSRV Docs License REUSE status

sysexits-rs (sysexits) 是一个提供系统退出码常量的库,这些常量定义在 <sysexits.h> 中。

该库实现了 Termination trait,因此可以从 main 函数返回。

用法

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

cargo add sysexits

Crate 特性

extended_io_error

启用依赖于 io_error_inprogressio_error_more 特性的功能。这也启用了 stdnightly 隐含此特性。

nightly

启用依赖于夜间版 Rust 的功能。

std

启用依赖于标准库的功能。默认启用。

no_std 支持

支持 no_std 模式。禁用 default 特性以启用此功能。

文档

查看文档获取更多详细信息。

最低支持的 Rust 版本

该库的最低支持 Rust 版本 (MSRV) 是 v1.85.0。

源代码

上游仓库位于。

更新日志

请查看 CHANGELOG.adoc。

贡献

请查看 CONTRIBUTING.adoc。

许可证

版权所有 © 2022 Shun Sakai 和其他贡献者 (见 AUTHORS.adoc)

该库根据 Apache License 2.0 或 MIT License 的条款分发。

该项目符合 REUSE Specification 版本 3.3。有关版权和许可信息的更多详细信息,请参阅单个文件的版权声明。

完整示例代码

use sysexits::ExitCode;

fn main() -> Result<(), ExitCode> {
    // 模拟一个成功操作
    if some_operation_succeeds() {
        Ok(())
    } else {
        // 返回标准化的退出码
        Err(ExitCode::DataErr) // 表示输入数据有误
    }
}

fn some_operation_succeeds() -> bool {
    // 这里模拟操作逻辑
    true // 或 false 来测试不同退出码
}

// 使用 Termination trait 的示例
fn main() -> ExitCode {
    if some_operation_succeeds() {
        ExitCode::Ok // 成功退出
    } else {
        ExitCode::Usage // 用法错误退出
    }
}

// 直接使用常量值的示例
fn main() {
    if some_operation_succeeds() {
        std::process::exit(sysexits::EX_OK as i32); // 成功退出
    } else {
        std::process::exit(sysexits::EX_USAGE as i32); // 用法错误退出
    }
}

完整示例代码:

use sysexits::ExitCode;

// 示例1: 使用 Result<(), ExitCode> 返回类型
fn main() -> Result<(), ExitCode> {
    println!("程序开始执行...");
    
    // 模拟数据处理操作
    let data = vec![1, 2, 3];
    if validate_data(&data) {
        println!("数据处理成功");
        Ok(())
    } else {
        println!("数据验证失败");
        Err(ExitCode::DataErr) // 返回数据错误退出码
    }
}

fn validate_data(data: &[i32]) -> bool {
    // 简单的数据验证逻辑
    !data.is_empty() && data.iter().all(|&x| x > 0)
}

// 示例2: 使用 Termination trait (直接返回 ExitCode)
fn main() -> ExitCode {
    println!("检查系统配置...");
    
    if check_system_config() {
        println!("系统配置正常");
        ExitCode::Ok // 成功退出
    } else {
        println!("系统配置错误");
        ExitCode::Config // 配置错误退出
    }
}

fn check_system_config() -> bool {
    // 检查系统配置的逻辑
    true
}

// 示例3: 直接使用常量值退出
fn main() {
    println!("执行用户命令...");
    
    match execute_user_command() {
        Ok(_) => {
            println!("命令执行成功");
            std::process::exit(sysexits::EX_OK as i32)
        },
        Err(ErrorType::Usage) => {
            println!("命令用法错误");
            std::process::exit(sysexits::EX_USAGE as i32)
        },
        Err(ErrorType::Io) => {
            println!("IO错误");
            std::process::exit(sysexits::EX_IOERR as i32)
        }
    }
}

enum ErrorType {
    Usage,
    Io
}

fn execute_user_command() -> Result<(), ErrorType> {
    // 模拟命令执行逻辑
    Ok(())
}

1 回复

Rust系统退出码管理库sysexits使用指南

概述

sysexits库为Rust程序提供了标准化的Unix/C风格程序退出状态码常量定义,帮助开发者编写符合Unix惯例的命令行应用程序。

主要特性

  • 提供符合sysexits.h标准的退出码常量
  • 类型安全的ExitCode枚举
  • 支持直接转换为std::process::ExitCode
  • 清晰的错误分类和语义含义

安装方法

在Cargo.toml中添加依赖:

[dependencies]
sysexits = "0.3"

基本使用方法

1. 导入库

use sysexits::ExitCode;
use std::process;

2. 使用预定义退出码

fn main() {
    // 成功退出
    if everything_ok() {
        process::exit(ExitCode::Ok.into());
    }
    
    // 命令行用法错误
    if invalid_arguments() {
        process::exit(ExitCode::Usage.into());
    }
    
    // 数据格式错误
    if data_corrupted() {
        process::exit(ExitCode::DataErr.into());
    }
}

3. 完整示例

use sysexits::ExitCode;
use std::process;
use std::env;

fn main() {
    let args: Vec<String> = env::args().collect();
    
    if args.len() < 2 {
        eprintln!("Usage: {} <filename>", args[0]);
        process::exit(ExitCode::Usage.into());
    }
    
    let filename = &args[1];
    let content = match std::fs::read_to_string(filename) {
        Ok(content) => content,
        Err(e) => {
            eprintln!("Cannot read file {}: {}", filename, e);
            process::exit(ExitCode::NoInput.into());
        }
    };
    
    if content.trim().is_empty() {
        eprintln!("File is empty");
        process::exit(ExitCode::DataErr.into());
    }
    
    println!("File content: {}", content);
    process::exit(ExitCode::Ok.into());
}

完整示例demo

use sysexits::ExitCode;
use std::process;
use std::env;
use std::fs;
use std::io;

// 自定义错误处理函数
fn handle_file_operation(filename: &str) -> Result<String, ExitCode> {
    // 检查文件是否存在
    if !fs::metadata(filename).is_ok() {
        eprintln!("错误:文件 '{}' 不存在", filename);
        return Err(ExitCode::NoInput);
    }
    
    // 读取文件内容
    let content = match fs::read_to_string(filename) {
        Ok(content) => content,
        Err(e) if e.kind() == io::ErrorKind::PermissionDenied => {
            eprintln!("错误:没有权限读取文件 '{}'", filename);
            return Err(ExitCode::NoPerm);
        }
        Err(e) => {
            eprintln!("错误:读取文件 '{}' 失败: {}", filename, e);
            return Err(ExitCode::IoErr);
        }
    };
    
    // 检查文件内容是否为空
    if content.trim().is_empty() {
        eprintln!("警告:文件 '{}' 内容为空", filename);
        return Err(ExitCode::DataErr);
    }
    
    Ok(content)
}

// 处理命令行参数
fn parse_arguments(args: &[String]) -> Result<String, ExitCode> {
    if args.len() != 2 {
        eprintln!("用法:{} <文件名>", args[0]);
        return Err(ExitCode::Usage);
    }
    
    Ok(args[1].clone())
}

fn main() {
    // 获取命令行参数
    let args: Vec<String> = env::args().collect();
    
    // 解析参数
    let filename = match parse_arguments(&args) {
        Ok(filename) => filename,
        Err(exit_code) => {
            process::exit(exit_code.into());
        }
    };
    
    // 处理文件操作
    match handle_file_operation(&filename) {
        Ok(content) => {
            // 成功读取文件内容
            println!("文件内容:\n{}", content);
            process::exit(ExitCode::Ok.into());
        }
        Err(exit_code) => {
            process::exit(exit_code.into());
        }
    }
}

// 单元测试
#[cfg(test)]
mod tests {
    use super::*;
    use std::fs::File;
    use std::io::Write;
    use tempfile::NamedTempFile;

    #[test]
    fn test_file_reading_success() {
        let mut temp_file = NamedTempFile::new().unwrap();
        writeln!(temp_file, "测试内容").unwrap();
        
        let result = handle_file_operation(temp_file.path().to_str().unwrap());
        assert!(result.is_ok());
    }

    #[test]
    fn test_file_not_exists() {
        let result = handle_file_operation("nonexistent_file.txt");
        assert_eq!(result.err(), Some(ExitCode::NoInput));
    }
}

常用退出码常量

常量 描述
ExitCode::Ok 0 程序成功执行
ExitCode::Usage 64 命令行用法错误
ExitCode::DataErr 65 输入数据错误
ExitCode::NoInput 66 输入文件不存在或不可读
ExitCode::NoUser 67 指定的用户不存在
ExitCode::NoHost 68 指定的主机不存在
ExitCode::Unavailable 69 服务不可用
ExitCode::Software 70 内部软件错误
ExitCode::OsErr 71 系统错误(无法创建文件等)
ExitCode::OsFile 72 关键系统文件不存在
ExitCode::CantCreat 73 无法创建输出文件
ExitCode::IoErr 74 输入/输出错误
ExitCode::TempFail 75 临时故障,建议重试
ExitCode::Protocol 76 远程协议错误
ExitCode::NoPerm 77 权限不足

最佳实践

  1. 使用有意义的退出码:不要总是返回0或1,使用具体的错误代码
  2. 错误信息输出到stderr:将错误信息输出到标准错误流
  3. 文档说明:在程序文档中说明可能返回的退出码含义

注意事项

  • 退出码范围应在0-255之间
  • 退出码0始终表示成功
  • 非零退出码表示不同程度的错误
  • 遵循Unix惯例有助于脚本编写和自动化

通过使用sysexits库,您可以编写出更加专业和符合Unix惯例的命令行应用程序。

回到顶部