golang现代化Shell交互式命令行UI开发插件库readline的使用
Golang 现代化 Shell 交互式命令行 UI 开发插件库 readline 的使用
简介
readline 是一个现代化的纯 Go 实现的 readline
shell 库,具有完整的 .inputrc
和传统 readline 命令/选项支持,并扩展了现代 shell 中常见的各种命令、选项和工具。其架构和补全系统深受 Z-Shell 的启发。
特性
核心功能
- 纯 Go 实现,几乎仅使用标准库
- 跨平台支持 (Linux/MacOS/Windows)
- 完整的
.inputrc
支持(所有命令/选项) - 广泛的测试套件和几乎全覆盖的核心代码
- 扩展的命令/选项列表(编辑/补全/历史)
- 完整的多行编辑/移动支持
- 在
$EDITOR
/$VISUAL
中支持命令行编辑 - 可编程 API,可安全访问核心组件
- 支持任意数量的历史记录源
Emacs/标准模式
- 原生 Emacs 命令
- Emacs 风格的宏引擎(不跨多个调用工作)
- 关键字切换(操作符、布尔值、十六进制/二进制/数字)带迭代
- 命令/模式光标状态指示器
- 完整的撤销/重做历史
- 命令状态/参数/迭代提示显示
Vim 模式
- 接近原生的 Vim 模式
- Vim 文本对象(代码块、单词/空白/shell 单词)
- 扩展的环绕选择/更改/添加功能,带高亮
- Vim 可视/操作符挂起模式和光标样式指示
- Vim 插入和替换(一次/多次)
- 所有 Vim 寄存器,带补全支持
- Vim 风格的宏录制 (
q<a>
) 和调用 (@<a>
)
界面
- 支持 PS1/PS2/RPROMPT/transient/tooltip 提示(兼容 oh-my-posh)
- 扩展的补全系统,基于键映射且可配置,易于填充和使用
- 多种补全显示样式,支持颜色
- 补全和历史记录的增量搜索系统和高亮(模糊搜索)
- 自动和上下文感知的后缀移除,实现高效的标志/路径/列表补全
- 可选的异步自动补全
- 内置和可编程的语法高亮
示例代码
下面是一个使用 readline 库的基本示例:
package main
import (
"fmt"
"github.com/reeflective/readline"
)
func main() {
// 创建一个新的 readline shell 实例
shell := readline.NewShell()
// 设置提示符
shell.SetPrompt("myapp> ")
// 主循环
for {
// 读取用户输入
line, err := shell.Readline()
if err != nil {
break
}
// 处理用户输入
if line == "exit" {
break
}
// 回显用户输入
fmt.Printf("You entered: %s\n", line)
}
}
高级示例 - 带补全功能
package main
import (
"fmt"
"github.com/reeflective/readline"
"github.com/reeflective/readline/completion"
)
func main() {
// 创建 shell 实例
shell := readline.NewShell()
shell.SetPrompt("demo> ")
// 设置补全函数
completer := completion.NewCompleter()
// 添加命令补全
commands := []string{"help", "exit", "list", "add", "delete"}
completer.AddCommand(commands...)
// 设置补全器
shell.SetCompleter(completer)
// 主循环
for {
line, err := shell.Readline()
if err != nil {
break
}
switch line {
case "exit":
return
case "help":
fmt.Println("Available commands:", commands)
default:
fmt.Println("Unknown command. Type 'help' for available commands.")
}
}
}
使用 Vim 模式
package main
import (
"fmt"
"github.com/reeflective/readline"
)
func main() {
// 创建 shell 实例
shell := readline.NewShell()
// 启用 Vim 模式
shell.SetKeyMap(readline.Vim)
shell.SetPrompt("vim-mode> ")
fmt.Println("Entering Vim mode. Press 'i' to insert, ESC to return to normal mode")
for {
line, err := shell.Readline()
if err != nil {
break
}
if line == "exit" {
break
}
fmt.Printf("You entered: %s\n", line)
}
}
历史记录功能示例
package main
import (
"fmt"
"github.com/reeflective/readline"
)
func main() {
shell := readline.NewShell()
shell.SetPrompt("history-demo> ")
// 添加一些历史记录
shell.AddHistory("first command")
shell.AddHistory("second command")
shell.AddHistory("third command")
for {
line, err := shell.Readline()
if err != nil {
break
}
if line == "exit" {
break
}
// 将命令添加到历史记录
shell.AddHistory(line)
fmt.Printf("Executed: %s\n", line)
}
}
多行编辑示例
package main
import (
"fmt"
"github.com/reeflective/readline"
)
func main() {
shell := readline.NewShell()
shell.SetPrompt("multiline> ")
shell.SetMultiLine(true) // 启用多行模式
fmt.Println("Enter multi-line input. Press Alt+Enter for new line, Enter to submit")
for {
lines, err := shell.Readline()
if err != nil {
break
}
if lines == "exit" {
break
}
fmt.Printf("You entered:\n%s\n", lines)
}
}
readline 库提供了丰富的功能来构建现代化的交互式命令行应用程序,包括高级编辑功能、补全系统、历史记录管理等。通过上述示例,您可以快速开始使用这个强大的库来增强您的命令行工具。
更多关于golang现代化Shell交互式命令行UI开发插件库readline的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html
1 回复
更多关于golang现代化Shell交互式命令行UI开发插件库readline的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
Golang 现代化 Shell 交互式命令行 UI 开发 - readline 库使用指南
在 Go 中开发交互式命令行应用时,readline
是一个功能强大的库,它提供了类似 Bash 的行编辑功能、历史记录、自动补全等特性。下面我将详细介绍如何使用这个库来构建现代化的命令行界面。
readline 库简介
readline
库是 Go 中一个流行的命令行交互库,主要特点包括:
- 行编辑功能(移动光标、删除字符等)
- 历史记录管理
- 自动补全支持
- 自定义提示符
- 支持彩色输出
安装
go get github.com/chzyer/readline
基础用法
package main
import (
"fmt"
"strings"
"github.com/chzyer/readline"
)
func main() {
// 创建 readline 实例
rl, err := readline.New("> ")
if err != nil {
panic(err)
}
defer rl.Close()
for {
// 读取用户输入
line, err := rl.Readline()
if err != nil { // io.EOF 或其他错误
break
}
line = strings.TrimSpace(line)
if line == "" {
continue
}
// 处理命令
switch line {
case "exit", "quit":
fmt.Println("Bye!")
return
default:
fmt.Printf("You typed: %q\n", line)
}
}
}
高级功能实现
1. 添加历史记录功能
func main() {
// 配置 readline
rl, err := readline.NewEx(&readline.Config{
Prompt: "> ",
HistoryFile: "/tmp/readline-history.tmp", // 历史记录文件
HistoryLimit: 1000, // 历史记录最大条数
AutoComplete: completer, // 自动补全器
InterruptPrompt: "^C", // Ctrl+C 提示
EOFPrompt: "exit", // EOF 提示
})
if err != nil {
panic(err)
}
defer rl.Close()
// 使用方式与之前相同
}
2. 实现自动补全
var completer = readline.NewPrefixCompleter(
readline.PcItem("mode",
readline.PcItem("vi"),
readline.PcItem("emacs"),
),
readline.PcItem("login"),
readline.PcItem("say",
readline.PcItem("hello"),
readline.PcItem("world"),
),
readline.PcItem("set"),
readline.PcItem("exit"),
readline.PcItem("quit"),
readline.PcItem("help"),
)
3. 自定义提示符(带颜色)
func dynamicPrompt() string {
return fmt.Sprintf("\033[31m> \033[32m%s\033[0m ", time.Now().Format("15:04:05"))
}
func main() {
rl, err := readline.NewEx(&readline.Config{
Prompt: dynamicPrompt(),
HistoryFile: "/tmp/readline-history.tmp",
AutoComplete: completer,
InterruptPrompt: "^C",
EOFPrompt: "exit",
// 启用动态提示
FuncFilterInputRune: func(r rune) (rune, bool) {
if r == readline.CharCtrlZ {
return r, false
}
return r, true
},
})
if err != nil {
panic(err)
}
defer rl.Close()
// 每行输入后更新提示
rl.Config.Listener = &readline.DefaultListener{}
// 使用方式与之前相同
}
4. 密码输入(隐藏输入)
func passwordInput(prompt string) (string, error) {
rl, err := readline.NewEx(&readline.Config{
Prompt: prompt,
EnableMask: true,
MaskRune: '*',
HistoryLimit: -1, // 不记录历史
})
if err != nil {
return "", err
}
defer rl.Close()
line, err := rl.Readline()
if err != nil {
return "", err
}
return strings.TrimSpace(line), nil
}
实际应用示例
下面是一个更完整的示例,模拟一个简单的 shell:
package main
import (
"fmt"
"strings"
"time"
"github.com/chzyer/readline"
)
var completer = readline.NewPrefixCompleter(
readline.PcItem("help"),
readline.PcItem("time"),
readline.PcItem("echo"),
readline.PcItem("exit"),
)
func main() {
rl, err := readline.NewEx(&readline.Config{
Prompt: "\033[31m»\033[0m ",
HistoryFile: "/tmp/readline-demo.tmp",
AutoComplete: completer,
InterruptPrompt: "^C",
EOFPrompt: "exit",
})
if err != nil {
panic(err)
}
defer rl.Close()
fmt.Println("Simple Shell v0.1")
fmt.Println("Type 'help' for more information")
for {
line, err := rl.Readline()
if err != nil { // io.EOF, readline.ErrInterrupt
break
}
line = strings.TrimSpace(line)
switch line {
case "":
continue
case "help":
fmt.Println("Available commands:")
fmt.Println(" help - show this help")
fmt.Println(" time - show current time")
fmt.Println(" echo - echo back the input")
fmt.Println(" exit - exit the program")
case "time":
fmt.Println(time.Now().Format("2006-01-02 15:04:05"))
case "echo":
fmt.Println("You said: " + line)
case "exit":
fmt.Println("Goodbye!")
return
default:
fmt.Println("Unknown command:", line)
}
}
}
最佳实践
- 错误处理:始终检查 readline 初始化错误
- 资源清理:使用 defer 关闭 readline 实例
- 历史记录:合理设置历史记录大小和存储位置
- 性能考虑:对于频繁更新的提示,考虑性能影响
- 跨平台:测试在不同终端下的表现
readline 库为 Go 命令行应用提供了强大的交互能力,结合其他库如 termui
或 tview
可以构建更丰富的终端界面。