如何用CommandLineUtils配置互斥且至少其一必填的命令行选项
问题
我正在维护一个使用CommandLineUtils解析命令行选项的项目,选项通过调用CommandLineApplication.Option()方法定义(而非类属性形式)。需要添加校验规则:要么必填-c选项,要么同时必填-a和-d选项——即必须满足其中一组要求,不能都不选,也不能不符合组合规则。已知.IsRequired()方法无法实现这种灵活的关联校验,请问该如何处理?
示例代码:
public void Configure(CommandLineApplication cmd) { var aOpt = cmd.Option("-a", "Option A", CommandOptionType.SingleValue).IsRequired(); var bOpt = cmd.Option("-b", "Option B", CommandOptionType.SingleValue); cmd.Command("subCommand1", subcmd => { var cOpt = subcmd.Option("-c", "Option C", CommandOptionType.SingleValue).IsRequired(); var dOpt = subcmd.Option("-d", "Option D", CommandOptionType.SingleValue).IsRequired(); }); cmd.Command("subCommand2", subcmd => { var fOpt = subcmd.Option("-f", "Option F", CommandOptionType.SingleValue).IsRequired(); }); }
解决方案
CommandLineUtils支持通过自定义逻辑实现复杂的选项关联校验,核心思路是在命令执行前检查选项的存在状态,手动校验组合规则,具体步骤如下:
1. 保留选项引用
首先要确保需要校验的选项变量(如aOpt、cOpt、dOpt)在后续校验逻辑的作用域内可用,避免链式调用后直接丢弃引用:
// 不要直接链式调用后丢弃,保留变量引用 var aOpt = cmd.Option("-a", "Option A", CommandOptionType.SingleValue); // 子命令内的选项同理,保留引用 cmd.Command("subCommand1", subcmd => { var cOpt = subcmd.Option("-c", "Option C", CommandOptionType.SingleValue); var dOpt = subcmd.Option("-d", "Option D", CommandOptionType.SingleValue); // 后续校验逻辑将使用这些变量 });
2. 添加自定义校验逻辑
可以通过两种方式触发校验:命令的OnExecute委托,或者全局的ValidationErrorHandler,推荐使用OnExecute(更贴合命令层级的逻辑)。
针对子命令的校验(以subCommand1为例)
如果规则适用于子命令subCommand1,直接在子命令的OnExecute中添加校验:
cmd.Command("subCommand1", subcmd => { var cOpt = subcmd.Option("-c", "Option C", CommandOptionType.SingleValue); var dOpt = subcmd.Option("-d", "Option D", CommandOptionType.SingleValue); // 保留根命令的aOpt引用(如果规则需要跨层级校验) var aOpt = cmd.Option("-a", "Option A", CommandOptionType.SingleValue); subcmd.OnExecute(() => { // 判断选项是否被提供(HasValue()适用于SingleValue类型) bool hasC = cOpt.HasValue(); bool hasA = aOpt.HasValue(); bool hasD = dOpt.HasValue(); // 校验组合规则:要么有-c,要么同时有-a和-d if (!hasC && !(hasA && hasD)) { subcmd.Error.WriteLine("错误:必须提供-c选项,或者同时提供-a和-d选项"); return 1; // 返回非0退出码表示执行失败 } // 这里添加子命令的正常执行逻辑 return 0; }); });
针对根命令的校验
如果规则适用于根命令,在根命令的OnExecute中添加相同逻辑即可:
cmd.OnExecute(() => { bool hasC = cOpt.HasValue(); bool hasA = aOpt.HasValue(); bool hasD = dOpt.HasValue(); if (!hasC && !(hasA && hasD)) { cmd.Error.WriteLine("错误:必须提供-c选项,或者同时提供-a和-d选项"); return 1; } // 根命令的正常执行逻辑 return 0; });
替代方案:使用ValidationErrorHandler
如果需要全局统一处理校验,可以设置命令的ValidationErrorHandler:
cmd.ValidationErrorHandler = (context) => { bool hasC = cOpt.HasValue(); bool hasA = aOpt.HasValue(); bool hasD = dOpt.HasValue(); if (!hasC && !(hasA && hasD)) { context.Command.Error.WriteLine("错误:必须提供-c选项,或者同时提供-a和-d选项"); return false; // 返回false表示校验不通过 } return true; };
关键说明
- 使用
HasValue()方法判断选项是否被用户提供(对于CommandOptionType.SingleValue类型,只要用户输入了该选项且有值,就会返回true) - 校验失败时,通过
Error.WriteLine输出错误信息,并返回非0退出码(或在ValidationErrorHandler返回false),CommandLineUtils会自动终止执行并提示用户 - 如果需要跨命令层级校验(比如子命令依赖根命令的选项),只要确保选项变量在作用域内即可访问
内容的提问来源于stack exchange,提问作者Rob Mosher
相关产品推荐
相关产品推荐

