golang命令行界面readline式交互插件库liner的使用

Golang命令行界面readline式交互插件库liner的使用

Liner是一个带有历史记录功能的命令行编辑器,它受到linenoise的启发。它适用于类Unix系统和Windows平台,旨在为跨平台应用程序提供统一的命令行编辑体验。

功能特点

Liner支持以下命令行编辑功能(在支持的平台和终端上):

快捷键 功能描述
Ctrl-A, Home 移动光标到行首
Ctrl-E, End 移动光标到行尾
Ctrl-B, Left 向左移动一个字符
Ctrl-F, Right 向右移动一个字符
Ctrl-Left, Alt-B 移动到前一个单词
Ctrl-Right, Alt-F 移动到下一个单词
Ctrl-D, Del 删除光标下的字符(如果行不为空)
Ctrl-D 文件结束(如果行为空)通常退出应用程序
Ctrl-C 重置输入(创建新的空提示)
Ctrl-L 清屏(行内容不变)
Ctrl-T 交换前一个字符和当前字符
Ctrl-H, BackSpace 删除光标前的字符
Ctrl-W, Alt-BackSpace 删除光标前的单词
Alt-D 删除光标后的单词
Ctrl-K 删除从光标到行尾的内容
Ctrl-U 删除从行首到光标的内容
Ctrl-P, Up 历史记录中的上一条匹配
Ctrl-N, Down 历史记录中的下一条匹配
Ctrl-R 反向搜索历史记录
Ctrl-Y 从Yank缓冲区粘贴
Tab 下一个补全选项
Shift-Tab 上一个补全选项

使用示例

下面是一个完整的Liner使用示例,展示了如何创建一个简单的交互式命令行界面:

package main

import (
	"log"
	"os"
	"path/filepath"
	"strings"

	"github.com/peterh/liner"
)

var (
	// 历史记录文件路径
	history_fn = filepath.Join(os.TempDir(), ".liner_example_history")
	// 自动补全的示例名称列表
	names      = []string{"john", "james", "mary", "nancy"}
)

func main() {
	// 创建一个新的Liner实例
	line := liner.NewLiner()
	// 确保在程序结束时关闭Liner
	defer line.Close()

	// 设置Ctrl+C可以中止输入
	line.SetCtrlCAborts(true)

	// 设置自动补全函数
	line.SetCompleter(func(line string) (c []string) {
		for _, n := range names {
			if strings.HasPrefix(n, strings.ToLower(line)) {
				c = append(c, n)
			}
		}
		return
	})

	// 尝试读取历史记录文件
	if f, err := os.Open(history_fn); err == nil {
		line.ReadHistory(f)
		f.Close()
	}

	// 显示提示并获取用户输入
	if name, err := line.Prompt("What is your name? "); err == nil {
		log.Print("Got: ", name)
		// 将输入添加到历史记录
		line.AppendHistory(name)
	} else if err == liner.ErrPromptAborted {
		log.Print("Aborted")
	} else {
		log.Print("Error reading line: ", err)
	}

	// 将历史记录写入文件
	if f, err := os.Create(history_fn); err != nil {
		log.Print("Error writing history file: ", err)
	} else {
		line.WriteHistory(f)
		f.Close()
	}
}

代码说明

  1. 首先创建一个liner.NewLiner()实例,并在程序结束时调用Close()
  2. SetCtrlCAborts(true)设置Ctrl+C可以中止输入
  3. SetCompleter()函数用于设置自动补全逻辑
  4. ReadHistory()WriteHistory()用于读取和保存历史记录
  5. Prompt()显示提示并等待用户输入
  6. AppendHistory()将用户输入添加到历史记录

这个示例展示了Liner的基本用法,包括历史记录管理、自动补全和基本的用户交互功能。


更多关于golang命令行界面readline式交互插件库liner的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于golang命令行界面readline式交互插件库liner的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


Golang命令行交互库Liner使用指南

Liner是一个轻量级的Go库,用于创建具有readline功能的命令行交互界面。它提供了行编辑、历史记录、自动补全等功能,非常适合构建交互式命令行工具。

安装Liner

go get github.com/peterh/liner

基础使用示例

package main

import (
	"fmt"
	"log"
	"strings"
	
	"github.com/peterh/liner"
)

func main() {
	line := liner.NewLiner()
	defer line.Close()

	line.SetCtrlCAborts(true) // 允许Ctrl+C终止程序

	// 设置自动补全函数
	line.SetCompleter(func(line string) (c []string) {
		commands := []string{"help", "exit", "show", "list", "add"}
		for _, cmd := range commands {
			if strings.HasPrefix(cmd, strings.ToLower(line)) {
				c = append(c, cmd)
			}
		}
		return
	})

	// 加载历史记录
	if f, err := liner.NewHistoryFromFile("history.txt"); err == nil {
		line.ReadHistory(f)
	}

	for {
		// 读取用户输入
		if input, err := line.Prompt("> "); err == nil {
			input = strings.TrimSpace(input)
			if input == "" {
				continue
			}
			
			// 添加到历史记录
			line.AppendHistory(input)
			
			// 处理命令
			switch input {
			case "exit":
				return
			case "help":
				fmt.Println("Available commands: help, exit, show, list, add")
			default:
				fmt.Printf("You entered: %q\n", input)
			}
		} else if err == liner.ErrPromptAborted {
			fmt.Println("Aborted")
			break
		} else {
			log.Print("Error reading line: ", err)
			break
		}
	}

	// 保存历史记录
	if f, err := liner.NewHistoryFile("history.txt", 0644); err == nil {
		line.WriteHistory(f)
		f.Close()
	} else {
		log.Print("Error writing history file: ", err)
	}
}

主要功能特性

1. 行编辑功能

Liner提供了类似readline的行编辑功能:

  • 左右箭头移动光标
  • Home/End键跳转到行首/行尾
  • Backspace/Delete删除字符
  • Ctrl+U删除整行
  • Ctrl+K删除光标到行尾

2. 历史记录

// 添加历史记录
line.AppendHistory("command")

// 保存历史记录到文件
if f, err := liner.NewHistoryFile("history.txt", 0644); err == nil {
    line.WriteHistory(f)
    f.Close()
}

// 从文件加载历史记录
if f, err := liner.NewHistoryFromFile("history.txt"); err == nil {
    line.ReadHistory(f)
}

3. 自动补全

line.SetCompleter(func(line string) (c []string) {
    // 返回匹配的补全选项
    if strings.HasPrefix("hello", strings.ToLower(line)) {
        c = append(c, "hello")
    }
    if strings.HasPrefix("world", strings.ToLower(line)) {
        c = append(c, "world")
    }
    return
})

4. 密码输入

password, err := line.PasswordPrompt("Enter password: ")
if err != nil {
    log.Fatal(err)
}
fmt.Printf("Your password is %d characters long\n", len(password))

5. 多行模式

line.SetMultiLineMode(true) // 启用多行模式
input, err := line.Prompt("multiline> ")

高级配置

// 自定义Tab键行为
line.SetTabCompletionStyle(liner.TabPrints)

// 设置最大历史记录数
line.SetHistoryLimit(1000)

// 自定义Ctrl+C行为
line.SetCtrlCAborts(true)

// 设置自定义提示符
prompt := func() string {
    return fmt.Sprintf("[%s] > ", time.Now().Format("15:04:05"))
}
input, err := line.PromptWithSuggestion(prompt(), "default", -1)

实际应用示例 - 简单的数据库CLI

package main

import (
	"fmt"
	"log"
	"strings"
	
	"github.com/peterh/liner"
)

type Database struct {
	data map[string]string
}

func NewDatabase() *Database {
	return &Database{
		data: make(map[string]string),
	}
}

func (db *Database) Set(key, value string) {
	db.data[key] = value
}

func (db *Database) Get(key string) (string, bool) {
	val, ok := db.data[key]
	return val, ok
}

func (db *Database) Delete(key string) {
	delete(db.data, key)
}

func (db *Database) Keys() []string {
	keys := make([]string, 0, len(db.data))
	for k := range db.data {
		keys = append(keys, k)
	}
	return keys
}

func main() {
	db := NewDatabase()
	line := liner.NewLiner()
	defer line.Close()

	line.SetCompleter(func(line string) (c []string) {
		commands := []string{"set", "get", "delete", "keys", "exit"}
		for _, cmd := range commands {
			if strings.HasPrefix(cmd, strings.ToLower(line)) {
				c = append(c, cmd)
			}
		}
		return
	})

	for {
		input, err := line.Prompt("db> ")
		if err != nil {
			if err == liner.ErrPromptAborted {
				fmt.Println("Bye!")
			} else {
				log.Printf("Error: %v", err)
			}
			break
		}

		input = strings.TrimSpace(input)
		parts := strings.Fields(input)
		if len(parts) == 0 {
			continue
		}

		cmd := strings.ToLower(parts[0])
		switch cmd {
		case "set":
			if len(parts) != 3 {
				fmt.Println("Usage: set <key> <value>")
				continue
			}
			db.Set(parts[1], parts[2])
			fmt.Println("OK")
		case "get":
			if len(parts) != 2 {
				fmt.Println("Usage: get <key>")
				continue
			}
			if val, ok := db.Get(parts[1]); ok {
				fmt.Println(val)
			} else {
				fmt.Println("(nil)")
			}
		case "delete":
			if len(parts) != 2 {
				fmt.Println("Usage: delete <key>")
				continue
			}
			db.Delete(parts[1])
			fmt.Println("OK")
		case "keys":
			keys := db.Keys()
			for _, k := range keys {
				fmt.Println(k)
			}
		case "exit":
			fmt.Println("Bye!")
			return
		default:
			fmt.Println("Unknown command. Available: set, get, delete, keys, exit")
		}

		line.AppendHistory(input)
	}
}

Liner是一个简单但功能强大的库,适合需要基本命令行交互功能的Go应用程序。它轻量级、易于使用,并且提供了足够的功能来构建复杂的CLI工具。

回到顶部