github.com/chzyer/readline 是一个采用纯 Go 语言开发的交互式命令行库,它完整实现了类似 GNU Readline 的功能特性,并具备跨平台终端操作能力。
一、核心功能
1)多平台支持
采用分平台文件实现(如 term_linux.go、term_windows.go),可跨平台支持终端原始模式控制,兼容 Linux/BSD/Windows 系统。
2)行编辑能力
支持 Emacs/Vi 风格的快捷键操作(例如 Ctrl+A 快速跳转至行首)。
内置 runebuf.go 模块实现高效的符文缓冲区管理,全面兼容 Unicode 字符输入。
3)自动补全系统
内置complete.go
实现了层级补全树功能,支持静态预定义补全和动态生成补全两种补全方式。
4)支持历史命令
按上下方向键可快速查看和切换历史输入命令。
二、关键组件
模块文件 | 功能描述 | 实现要点 |
---|---|---|
operation.go | 处理用户输入操作 | 封装 Ctrl+C 等中断信号 |
history.go | 命令历史管理 | 支持持久化到文件 |
password.go | 安全密码输入 | 屏蔽回显并自定义提示 |
search.go | 历史命令搜索 | 支持增量搜索匹配 |
三、realine基本用法
编写一个简单交互式命令行,支持ip和iptables命令补全功能,默认支持按上下方向键切换历史输入命令。代码示例如下:
package main
import (
"fmt"
"github.com/chzyer/readline"
)
func main() {
// 内置补全命令,安tab键补全
var completer = readline.NewPrefixCompleter(
// 补全命令ip,支持ip addr 和 ip link两种命令
readline.PcItem("ip",
readline.PcItem("addr"),
readline.PcItem("link"),
),
// 补全命令iptables,支持iptables stop 和 iptables start两种命令
readline.PcItem("iptables",
readline.PcItem("stop"),
readline.PcItem("start"),
),
)
rl, err := readline.NewEx(&readline.Config{
InterruptPrompt: "^C", // 支持ctrl + c关闭命令行
AutoComplete: completer, // 设置补充命令
})
if err != nil {
fmt.Println("err:", err)
return
}
defer rl.Close()
// 交互循环
for {
rl.SetPrompt("输入 > ")
// 接收数据
line, err := rl.Readline()
if err != nil {
break
}
println(" 接收:", line)
}
}
运行之后,直接按tab键,会显示ip和iptables提示,如下所示:
当选择ip命令后,继续按tab键,会显示ip之后可加的addr和link提示,如下所示:
可通过HistoryFile将历史命令进行文件存储,结合HistoryLimit限制历史命令存储个数,代码示例如下:
rl, err := readline.NewEx(&readline.Config{
InterruptPrompt: "^C", // 支持ctrl + c关闭命令行
AutoComplete: completer, // 设置补充命令
HistoryFile: "/tmp/.app_history", // 历史命令存储文件
HistoryLimit: 1000, // 限制1000条历史
})
四、高级特性
1)动态补全实现
通过回调函数实时生成补全项(如文件系统补全),代码示例如下:
func listFiles() []readline.PrefixCompleterInterface {
files, _ := os.ReadDir(".")
var items []readline.PrefixCompleterInterface
for _, f := range files {
items = append(items, readline.PcItem(f.Name()))
}
return items
}
func main() {
// 配置补全器时注入动态生成函数
completer = readline.NewPrefixCompleter(
readline.PcItem("ls", listFiles()...),
)
rl, err := readline.NewEx(&readline.Config{
InterruptPrompt: "^C", // 支持ctrl + c关闭命令行
AutoComplete: completer, // 设置补充命令
HistoryFile: "/tmp/.app_history", // 历史命令存储文件
HistoryLimit: 1000, // 限制1000条历史
})
if err != nil {
fmt.Println("err:", err)
return
}
defer rl.Close()
// 交互循环
for {
rl.SetPrompt("输入 > ")
// 接收数据
line, err := rl.Readline()
if err != nil {
break
}
println(" 接收:", line)
}
}
执行结果如下:
2)多模式切换
实现了vi和emacs两种编辑模式的切换,支持 Emacs/Vi 风格的快捷键操作,通过调用SetVimMode()函数切换输入模式,并利用监听器实时更新提示符显示。代码示例如下:
func main() {
rl, err := readline.NewEx(&readline.Config{
InterruptPrompt: "^C", // 支持ctrl + c关闭命令行
})
if err != nil {
fmt.Println("err:", err)
return
}
defer rl.Close()
// 交互循环
for {
rl.SetPrompt("输入 > ")
// 接收数据
line, err := rl.Readline()
if err != nil {
break
}
if strings.HasPrefix(line, "mode ") {
switch line[5:] {
case "vi":
rl.SetVimMode(true)
case "emacs":
rl.SetVimMode(false)
}
}
println(" 接收:", line)
}
}
3)安全密码输入
readline库提供了专门的密码输入功能,支持自定义密码输入时的提示和显示。代码示例如下:
func main() {
rl, err := readline.NewEx(&readline.Config{
InterruptPrompt: "^C", // 支持ctrl + c关闭命令行
})
if err != nil {
fmt.Println("err:", err)
return
}
defer rl.Close()
setPasswordCfg := rl.GenPasswordConfig()
setPasswordCfg.SetListener(func(line []rune, pos int, key rune) (newLine []rune, newPos int, ok bool) {
rl.SetPrompt(fmt.Sprintf("输入密码(%v): ", len(line)))
rl.Refresh()
return nil, 0, false
})
// 交互循环
for {
// 接收数据
line, err := rl.ReadPasswordWithConfig(setPasswordCfg)
if err != nil {
break
}
fmt.Printf("密码:%s", line)
}
}
能够实施显示输入的密码长度,执行结果如下:
4)彩色输出渲染
通过ANSI代码实现终端着色,代码示例如下:
func main() {
rl, err := readline.NewEx(&readline.Config{
InterruptPrompt: "^C", // 支持ctrl + c关闭命令行
})
if err != nil {
fmt.Println("err:", err)
return
}
defer rl.Close()
rl.Write([]byte("\033[31m红色错误信息\033[0m\n"))
rl.SetPrompt("\033[32m绿色提示 > \033[0m")
// 交互循环
for {
// 接收数据
line, err := rl.Readline()
if err != nil {
break
}
fmt.Printf(" 输入:%s\n", line)
}
}
执行结果如下: