ShellLibrary.Cmd 是一个为 .NET 应用程序设计的声明式交互式 Shell 框架。你只需要进行简单的配置和命令注册,就能为你的应用快速嵌入一个功能完整、风格可定制的命令行界面(CLI)。它支持管道、外部二进制调用、打字机效果、安全锁定以及批处理脚本执行等特性。
首先,在你的项目中引用 ShellLibrary.Cmd 命名空间:
using ShellLibrary.Cmd;然后,通过静态的 MainLibrary.BuildShell 来配置 Shell 的基本属性并启动它:
// 获取全局 Shell 设置实例
var setting = MainLibrary.BuildShell.ShellSetting;
// 配置 Shell 的外观信息
setting.ShellName = "TutorialShell";
setting.ShellVerion = "0.1";
setting.ShellDescription = "ShellLibrary.Cmd 教程示例";
setting.ShellWelcomeTemplate = true; // 显示欢迎屏幕
setting.ShellTypwriterstyle = true; // 启用打字机效果
// (可选)获取命令注册器并添加命令(详见第 2 节)
var registrar = new Register.CommandInfoMake();
registrar.MakeCommandInfo(...);
// 启动 Shell 交互循环
var shell = new MainLibrary.BuildShell();
await shell.BuildAsync();运行后,你会看到一个带有欢迎屏幕和提示符 > 的命令行交互界面。
所有 Shell 内部的命令都需通过 Register.CommandInfoMake 进行注册。每个命令包含名称、描述、用法、帮助文本以及一个执行委托 Func<string?, string[], Task<string>>。
下面的例子注册了一个 echo 命令,它将用户输入的参数拼接后输出到控制台,并将该字符串作为返回值(用于管道传递)。
var registrar = new Register.CommandInfoMake();
registrar.MakeCommandInfo(
name: "echo",
description: "输出给定的文本到控制台。",
usage: "echo <text>",
commandHelp: "将后续所有参数拼接为字符串并输出。",
commandAction: async (stdin, args) =>
{
var output = string.Join(" ", args);
Console.WriteLine(output);
return output;
}
);stdin:来自管道中上一个命令传递过来的标准输入字符串。如果该命令不是管道中的一环,则该参数为null。args:用户输入中除去命令名以外的参数数组。- 返回值:一个字符串,它会作为当前命令的输出,传递给管道中的下一个命令。如果命令位于管道末尾,则返回值被忽略。
注意:ShellLibrary.Cmd 不会自动将命令返回值打印到控制台。如果你想在控制台上看到输出,必须在命令委托中主动调用
Console.Write或Console.WriteLine。
如果你有大量命令,可以使用 BatchMakeCommandInfo 或 AllListCommandInfo 进行批量注册。
var commandList = new List<Register.CommandRegisterInfo>
{
new Register.CommandRegisterInfo { Name = "cmd1", ... },
new Register.CommandRegisterInfo { Name = "cmd2", ... }
};
registrar.BatchMakeCommandInfo(commandList);ShellLibrary.Cmd 原生支持使用管道符(默认 |)连接多个命令。在解析时,前一个命令的返回值会被自动传递给后一个命令的 stdin 参数。
示例:假设我们注册了 reverse 命令,它反转输入的字符串。
registrar.MakeCommandInfo(
name: "reverse",
description: "反转输入的文本。",
usage: "reverse",
commandHelp: "将标准输入或参数中的文本反转。",
commandAction: async (stdin, args) =>
{
var input = stdin ?? string.Join(" ", args);
var reversed = new string(input.Reverse().ToArray());
Console.WriteLine(reversed);
return reversed;
}
);用户即可使用管道组合命令:
> echo hello world | reverse
dlrow olleh
你可以通过 MainLibrary.BuildShell.ShellSetting.ShellDelimiter 更改管道分隔符。
默认情况下,如果用户输入的命令名是一个存在于磁盘上的文件路径(例如 ping 或 /bin/ls),ShellLibrary.Cmd 会尝试启动该文件作为外部进程执行。
此行为由 ShellSetting.ShellRunBinary 控制:
true(默认):允许执行外部可执行文件。false:禁止执行任何外部二进制,仅执行内部注册的命令。这有助于创建安全的“受限” Shell 环境。
MainLibrary.BuildShell.ShellSetting.ShellRunBinary = false;当用户试图执行外部程序时,Shell 将返回 “Command not found.”。
通过配置 TypeWriter 和 ShellSetting,你可以控制 Shell 的视觉效果。
setting.ShellTypwriterstyle = true;启用后,欢迎屏幕的文字会逐字打印,营造复古终端体验。你也可以在自定义命令中调用 MainLibrary.BuildShell.TypeWriter.Write 实现类似效果。
| 属性 | 说明 |
|---|---|
ShellCursorVisible |
是否显示控制台光标(true / false) |
ShellTitle |
是否将 Shell 名称设置为控制台窗口标题 |
ShellWelcomeTemplate |
是否在启动时显示欢迎屏幕 |
setting.ShellCursorVisible = true;
setting.ShellTitle = true;ShellLibrary.Cmd 提供两种维度的安全锁定机制。
如第 4 节所述,设置 ShellRunBinary = false 可以完全阻止用户启动任何外部程序。
由于命令必须显式注册,你可以通过只注册少量安全命令,来构建一个功能极简的受限 Shell。结合 ShellRunBinary = false,用户即使知道系统命令的路径也无法执行。
setting.ShellRunBinary = false;
registrar.MakeCommandInfo("ls", ...); // 只允许 ls这种方案比传统 rbash 更加彻底,因为用户无法通过任何方式接触到未注册的命令。
ShellLibrary.Cmd 自动记录用户输入的历史命令。记录保存在当前目录的 history.txt 文件中,每条记录附带时间戳。
你可以通过 MainLoop 实例控制该行为:
MainLibrary.BuildShell.MainLoop.HistoryCommandWriteEnabled = false; // 关闭历史记录Shell 内部处理了 Ctrl+C 信号。按一次不会退出;连续按两次将触发安全退出逻辑:先尝试终止正在运行的外部进程(如果有),然后退出 Shell。
你可以通过 MainLoop 的属性进行调整:
var loop = MainLibrary.BuildShell.MainLoop;
loop.SafeExitEnabled = true; // 是否启用安全退出(按两次 Ctrl+C)
loop.EnabledShellExit = true; // 是否允许退出 ShellShellLibrary.Cmd 包含一个实验性的批处理脚本解析器 SpecializedControlScriptParser,它可以将文本文件中每一行作为命令送入 Shell 执行。
var parser = new SpecializedControlScriptParser();
await parser.Parser("commands.txt");这对于自动化任务或 CI 场景非常实用。注意:该功能在 0.1.0 版本中已稳定可用,适合小型脚本文件。
MainLibrary.BuildShell.MainLoop.GetTipText = "$ "; // 默认为 ">"MessageReops 类包含一个字典,你可以覆盖默认的错误提示。
MainLibrary.BuildShell.MessageReops.Messages["CommandNotFound"] = "未知命令,请输入 help 查看可用命令。";using ShellLibrary.Cmd;
using ShellLibrary.Cmd.Command;
// 配置 Shell
var setting = MainLibrary.BuildShell.ShellSetting;
setting.ShellName = "MiniShell";
setting.ShellVerion = "1.0";
setting.ShellDescription = "一个带帮助系统的示例 Shell";
setting.ShellWelcomeTemplate = true;
setting.ShellTypwriterstyle = true;
setting.ShellRunBinary = false;
var registrar = new Register.CommandInfoMake();
// 注册 echo 命令
registrar.MakeCommandInfo("echo", "输出文本。", "echo <text>", "打印所有参数。",
async (stdin, args) => { var s = string.Join(" ", args); Console.WriteLine(s); return s; });
// 注册 clear 命令
registrar.MakeCommandInfo("clear", "清屏。", "clear", "清除控制台内容。",
async (stdin, args) => { Console.Clear(); return ""; });
// 注册 help 命令
registrar.MakeCommandInfo("help", "显示帮助。", "help", "列出所有命令。",
async (stdin, args) =>
{
foreach (var cmd in MainLibrary.BuildShell.CommandRepository.Commands)
Console.WriteLine($"{cmd.Key,-10} - {cmd.Value.Description}");
return "";
});
// 启动
var shell = new MainLibrary.BuildShell();
await shell.BuildAsync();ShellLibrary.Cmd 的设计目标是让 .NET 开发者能够以最小的代价,为应用程序赋予一个专业、安全且高度可定制的命令行交互界面。无论是用于调试、管理工具、游戏控制台还是嵌入式设备,它都能提供一套一致且强大的解决方案。
关于 API 的更多细节,请查阅源码注释或项目的 GitHub 仓库。