30 秒上手
KShell 自带脚手架命令,一条命令生成可运行的插件骨架:
$ plugin new myplugin # 生成插件骨架
$ plugin reload myplugin # 加载(改代码后重复执行即可热重载)
$ myplugin hello # 运行你的命令
hello hello(配置 example=value)
$ plugin info myplugin # 查看注册结果与配置项
plugin.json—— 元数据、命令声明、默认配置plugin.py—— Python 实现,每个命令一个函数
插件结构
KShell 按以下顺序搜索插件目录,先找到的优先(同名插件不会被后面的覆盖):
| 顺序 | 目录 | 用途 |
|---|---|---|
| 1 | 打包内置目录 | 随 KShell 发布的官方示例 |
| 2 | 项目根/plugins/ | 源码运行时使用 |
| 3 | 可执行文件同级/plugins/ | 推荐放自己的插件 |
| 4 | ~/.kshell/plugins/ | 用户级插件 |
| 5 | plugins.dirs 配置项 | 自定义目录 |
$ plugin dirs # 查看本机的实际搜索路径
plugin.json 字段
{
"name": "myplugin", // 必须与目录名一致
"version": "1.0.0",
"author": "你的名字",
"description": "插件的一句话说明",
"kshell": ">=2.1.0", // 要求的 KShell 版本
"enabled": true,
"commands": {
"myplugin": {
"function": "cmd_main", // 默认 cmd_<命令名>
"usage": "myplugin [参数] [-v]",
"description": "命令说明",
"aliases": ["mp"]
}
},
"settings": { // 默认配置,用户可覆盖
"greeting": "Hello",
"count": 3
}
}
| 字段 | 必需 | 说明 |
|---|---|---|
name | 是 | 插件名,必须与目录名一致;仅允许字母、数字、_、- |
version | 否 | 建议语义化版本 |
author / description | 否 | 显示在 plugin list |
homepage | 否 | 主页或仓库地址 |
kshell | 否 | 支持 >= <= > < == !=;只写版本号视为最低版本 |
enabled | 否 | 默认是否启用(用户配置优先级更高) |
commands | 否 | 省略则自动发现 cmd_* 函数 |
settings | 否 | 默认配置项,用户可用 plugin config 覆盖 |
commands 的三种写法
① 字符串数组
"commands": ["hello", "greet"]
自动对应 cmd_hello() 与 cmd_greet()
② 对象(可指定细节)
"commands": {
"pwd-strength": {
"function": "cmd_strength",
"aliases": ["pws"]
}
}
命令名与函数名可以不同,还能加别名
③ 省略(自动发现)
// 不写 commands 字段
// cmd_auto_one → 命令 auto-one
扫描 plugin.py 中所有 cmd_xxx 函数
plugin.py 实现
每个命令对应一个函数,签名固定为 (args, options, ctx):
def cmd_hello(args, options, ctx):
# 读取插件配置(plugin.json 的 settings,可被用户覆盖)
greeting = ctx.get('greeting', 'Hello')
# args 是位置参数列表,options 是选项字典
name = args[0] if args else 'World'
verbose = 'v' in options
lines = [f'{greeting}, {name}!']
if verbose:
lines.append(f'插件目录: {ctx.dir}')
return 0, '\n'.join(lines)
def on_load(ctx):
"""可选:插件加载完成时调用"""
ctx.log('myplugin 已加载')
KShell 把短选项一律视为布尔标志,因此需要带值的选项请用长选项:
$ mycmd -v # options = {'v': True}
$ mycmd -l 24 # options = {'l': True}, args = ['24'] ← 24 是位置参数
$ mycmd --length 24 # options = {'length': '24'} ✓ 推荐
$ mycmd --length=24 # options = {'length': '24'} ✓ 推荐
ctx 上下文 API
| 成员 | 说明 |
|---|---|
ctx.name / ctx.version | 插件名 / 版本 |
ctx.dir | 插件目录(pathlib.Path) |
ctx.plugin | 插件对象,含 manifest 原始 JSON |
ctx.get(key, default) | 读取插件配置(默认值 + 用户覆盖) |
ctx.set(key, value) | 写入插件配置(持久化) |
ctx.config() | 取全部配置的字典 |
ctx.fs | 文件系统对象(getcwd() list_dir() read_file() …) |
ctx.platform | 平台信息(system home_dir is_windows() …) |
ctx.settings | 全局设置(get()) |
ctx.colorize(text, key) | 按主题着色 |
ctx.log(msg) | 记录日志(加载阶段会打印) |
ctx.command_exists(name) | 命令是否已存在 |
ctx.register_command(name, fn, usage, desc) | 运行期动态注册命令 |
可用着色 key:
successerrorwarninginfo
titledirfilelinkcommand
返回值约定
| 返回 | 效果 |
|---|---|
return 0, "文本" | 成功,输出文本 |
return 1, "错误" | 失败,输出错误信息 |
return "文本" | 等价于 (0, "文本") |
return None | 等价于 (0, "") |
return 3 | 退出码 3,无输出 |
插件异常会被自动捕获并隔离,不会导致 Shell 崩溃:
$ boom
[插件 boom] 命令 boom 执行出错 → ValueError: 故意炸掉
官方示例插件
随 KShell 发布的三个插件,源码即是教材:
演示配置读取、选项解析、返回值与 on_load 回调。
def cmd_hello(args, options, ctx):
greeting = ctx.get('greeting', 'Hello')
punctuation = ctx.get('punctuation', '!')
name = args[0] if args else 'World'
if ctx.get('uppercase_name', False):
name = name.upper()
lines = [f'{greeting}, {name}{punctuation}']
if 'v' in options:
lines.append(f'插件目录: {ctx.dir}')
return 0, '\n'.join(lines)
用法:hello KShell ·
配置:plugin config hello greeting=你好
一个插件注册 3 条命令,命令名与函数名不同,使用加密安全随机源。
# plugin.json 中声明:
# pwgen → cmd_pwgen
# uuid → cmd_uuid
# passwd-strength → cmd_strength
import secrets
def _generate(length, use_symbols=True):
pool = string.ascii_letters + string.digits
if use_symbols:
pool += '!@#$%^&*()-_=+'
return ''.join(secrets.choice(pool) for _ in range(length))
def cmd_pwgen(args, options, ctx):
length = int(ctx.get('default_length', 16))
return 0, _generate(length, ctx.get('use_symbols', True))
用法:pwgen 3 20 · uuid · passwd-strength A1b2C3!@
--json 输出机器可读数据,配置项控制是否显示磁盘信息,取不到的信息自动跳过。
def cmd_sysinfo(args, options, ctx):
info = _collect(ctx) # 失败项自动跳过
if 'json' in options: # --json
return 0, json.dumps(info, ensure_ascii=False, indent=2)
width = max(len(k) for k in info)
lines = [ctx.colorize('系统信息', 'title'), '']
for key, value in info.items():
lines.append(f' {key:<{width}} : {value}')
return 0, '\n'.join(lines)
用法:sysinfo · sysinfo --json · 配置:plugin config sysinfo show_disk=false
插件画廊
正在加载社区插件…
提交你的插件
-
把插件目录推到 GitHub
推荐仓库根目录就是插件目录,方便用户直接克隆到
plugins/下。 -
README 中说明清楚
插件名、命令列表、KShell 版本要求、配置项、依赖。
-
登记到社区
在 GitHub Issues 提交,附上仓库地址与说明;通过审核后会出现在上方画廊。
plugin.json中name与目录名一致- 所有
commands声明的函数都真实存在 plugin list中状态为「已加载」- 每条命令都试过:正常参数、缺参数、错误参数
- 没有硬编码密钥
- 已声明
kshell最低版本
# 本地自检
$ plugin list # 状态应为「已加载」
$ plugin info myplugin # 确认命令与配置项
$ plugin reload myplugin # 确认无语法错误
安全须知
插件可以读写文件、执行命令、访问网络。KShell 目前不提供插件沙箱。
- 只安装信任的插件 —— 安装前建议阅读
plugin.py源码 - 不要硬编码密钥 —— 用
ctx.get()让用户在配置中填写 - 生成密码用
secrets—— 不要用random - 注意异常隔离的边界 —— 异常会被捕获,但死循环、
sys.exit()、大量内存分配仍会影响 Shell - 隔离环境验证 —— 运行不受信任的插件前,先在容器或虚拟机中评估