开发者社区 · KShell v2.1.0 插件系统

用 JSON + Python
扩展你的终端

插件 = 一个 plugin.json(元数据与配置)+ 一个 plugin.py(实现)。 不需要修改 KShell 本体,放进 plugins/ 目录即可被自动发现、加载与热重载。

— 个社区插件 3 个官方示例 2 个文件即可发布

30 秒上手

KShell 自带脚手架命令,一条命令生成可运行的插件骨架:

$ plugin new myplugin          # 生成插件骨架
$ plugin reload myplugin       # 加载(改代码后重复执行即可热重载)
$ myplugin hello                # 运行你的命令
hello hello(配置 example=value)

$ plugin info myplugin         # 查看注册结果与配置项
生成的两个文件
  • plugin.json —— 元数据、命令声明、默认配置
  • plugin.py —— Python 实现,每个命令一个函数

插件结构

plugins/
└── myplugin/
    ├── plugin.json 必需 · 元数据与配置
    └── plugin.py 必需 · Python 实现

KShell 按以下顺序搜索插件目录,先找到的优先(同名插件不会被后面的覆盖):

顺序目录用途
1打包内置目录随 KShell 发布的官方示例
2项目根/plugins/源码运行时使用
3可执行文件同级/plugins/推荐放自己的插件
4~/.kshell/plugins/用户级插件
5plugins.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

提交你的插件

  1. 把插件目录推到 GitHub

    推荐仓库根目录就是插件目录,方便用户直接克隆到 plugins/ 下。

  2. README 中说明清楚

    插件名、命令列表、KShell 版本要求、配置项、依赖。

  3. 登记到社区

    在 GitHub Issues 提交,附上仓库地址与说明;通过审核后会出现在上方画廊。

发布前自查
  • plugin.json 中 name 与目录名一致
  • 所有 commands 声明的函数都真实存在
  • plugin list 中状态为「已加载」
  • 每条命令都试过:正常参数、缺参数、错误参数
  • 没有硬编码密钥
  • 已声明 kshell 最低版本
# 本地自检
$ plugin list                  # 状态应为「已加载」
$ plugin info myplugin         # 确认命令与配置项
$ plugin reload myplugin       # 确认无语法错误

安全须知

⚠️ 插件是普通 Python 代码,拥有与 KShell 相同的权限

插件可以读写文件、执行命令、访问网络。KShell 目前不提供插件沙箱。

  • 只安装信任的插件 —— 安装前建议阅读 plugin.py 源码
  • 不要硬编码密钥 —— 用 ctx.get() 让用户在配置中填写
  • 生成密码用 secrets —— 不要用 random
  • 注意异常隔离的边界 —— 异常会被捕获,但死循环、sys.exit()、大量内存分配仍会影响 Shell
  • 隔离环境验证 —— 运行不受信任的插件前,先在容器或虚拟机中评估