ishell是一個用于創(chuàng)建交互式cli應(yīng)用程序的交互式shell庫。
最近在研究supervisor的源碼,參考supervisor的架構(gòu),做公司的項目。我后面會給出supervisor的開源學(xué)習(xí)的總結(jié)绊袋。github上有一個gopher寫了一個golang版的supervisor,源碼,原理和python版的都類似铸鹰,但是 ctl是執(zhí)行命令的方式癌别,不是很優(yōu)雅。
今天這篇文章介紹一個go的包蹋笼,實現(xiàn)交互式的CLI工具的包展姐。
常見的cli包有:flag、cli剖毯、os...都可以實現(xiàn)
但是上面有一個問題圾笨,就是執(zhí)行完以后,就會給出結(jié)果逊谋,并退出擂达,不是進(jìn)入一個shell中,執(zhí)行所有結(jié)果都是不同的胶滋。
交互式的cli如下:
今天要介紹的庫是 ishell
類似上面的gif圖中效果板鬓,很容易實現(xiàn)
代碼示例
import "strings"
import "github.com/abiosoft/ishell"
func main(){
// create new shell.
// by default, new shell includes 'exit', 'help' and 'clear' commands.
shell := ishell.New()
// display welcome info.
shell.Println("Sample Interactive Shell")
// register a function for "greet" command.
shell.AddCmd(&ishell.Cmd{
Name: "greet",
Help: "greet user",
Func: func(c *ishell.Context) {
c.Println("Hello", strings.Join(c.Args, " "))
},
})
// run shell
shell.Run()
}
上面代碼很簡單就是先實例化ishell.New()
一個 Shell
對象,使用方法AddCmd
添加命令
看一下源碼:
// New creates a new shell with default settings. Uses standard output and default prompt ">> ".
func New() *Shell {
return NewWithConfig(&readline.Config{Prompt: defaultPrompt})
}
// NewWithConfig creates a new shell with custom readline config.
func NewWithConfig(conf *readline.Config) *Shell {
rl, err := readline.NewEx(conf)
if err != nil {
log.Println("Shell or operating system not supported.")
log.Fatal(err)
}
return NewWithReadline(rl)
}
// NewWithReadline creates a new shell with a custom readline instance.
func NewWithReadline(rl *readline.Instance) *Shell {
shell := &Shell{
rootCmd: &Cmd{},
reader: &shellReader{
scanner: rl,
prompt: rl.Config.Prompt,
multiPrompt: defaultMultiPrompt,
showPrompt: true,
buf: &bytes.Buffer{},
completer: readline.NewPrefixCompleter(),
},
writer: rl.Config.Stdout,
autoHelp: true,
}
shell.Actions = &shellActionsImpl{Shell: shell}
shell.progressBar = newProgressBar(shell)
addDefaultFuncs(shell)
return shell
}
func (s *Shell) AddCmd(cmd *Cmd) {
s.rootCmd.AddCmd(cmd)
}
// AddCmd adds cmd as a subcommand.
func (c *Cmd) AddCmd(cmd *Cmd) {
if c.children == nil {
c.children = make(map[string]*Cmd)
}
c.children[cmd.Name] = cmd
}
再看一下shell的結(jié)構(gòu)體:
type Shell struct {
rootCmd *Cmd
generic func(*Context)
interrupt func(*Context, int, string)
interruptCount int
eof func(*Context)
reader *shellReader
writer io.Writer
active bool
activeMutex sync.RWMutex
ignoreCase bool
customCompleter bool
multiChoiceActive bool
haltChan chan struct{}
historyFile string
autoHelp bool
rawArgs []string
progressBar ProgressBar
pager string
pagerArgs []string
contextValues
Actions
}
執(zhí)行的結(jié)果:
Sample Interactive Shell
>>> help
Commands:
clear clear the screen
greet greet user
exit exit the program
help display help
>>> greet Someone Somewhere
Hello Someone Somewhere
>>> exit
$
常用的屬性
1. 輸入數(shù)據(jù)或密碼
shell.AddCmd(&ishell.Cmd{
Name: "login",
Func: func(c *ishell.Context) {
c.ShowPrompt(false)
defer c.ShowPrompt(true)
c.Println("Let's simulate login")
// prompt for input
c.Print("Username: ")
username := c.ReadLine()
c.Print("Password: ")
password := c.ReadPassword()
// do something with username and password
c.Println("Your inputs were", username, "and", password+".")
},
Help: "simulate a login",
})
2. 輸入可以換行
// read multiple lines with "multi" command
shell.AddCmd(&ishell.Cmd{
Name: "multi",
Help: "input in multiple lines",
Func: func(c *ishell.Context) {
c.Println("Input multiple lines and end with semicolon ';'.")
// 設(shè)置結(jié)束符
lines := c.ReadMultiLines(";")
c.Println("Done reading. You wrote:")
c.Println(lines)
},
})
3. 單選
// choice
shell.AddCmd(&ishell.Cmd{
Name: "choice",
Help: "multiple choice prompt",
Func: func(c *ishell.Context) {
choice := c.MultiChoice([]string{
"Golangers",
"Go programmers",
"Gophers",
"Goers",
}, "What are Go programmers called ?")
if choice == 2 {
c.Println("You got it!")
} else {
c.Println("Sorry, you're wrong.")
}
},
})
4. 多選
// multiple choice
shell.AddCmd(&ishell.Cmd{
Name: "checklist",
Help: "checklist prompt",
Func: func(c *ishell.Context) {
languages := []string{"Python", "Go", "Haskell", "Rust"}
choices := c.Checklist(languages,
"What are your favourite programming languages ?",
nil)
out := func() (c []string) {
for _, v := range choices {
c = append(c, languages[v])
}
return
}
c.Println("Your choices are", strings.Join(out(), ", "))
},
})
5. 顏色
cyan := color.New(color.FgCyan).SprintFunc()
yellow := color.New(color.FgYellow).SprintFunc()
boldRed := color.New(color.FgRed, color.Bold).SprintFunc()
shell.AddCmd(&ishell.Cmd{
Name: "color",
Help: "color print",
Func: func(c *ishell.Context) {
c.Print(cyan("cyan\n"))
c.Println(yellow("yellow"))
c.Printf("%s\n", boldRed("bold red"))
},
})
6. 進(jìn)度條
// progress bars
{
// determinate
shell.AddCmd(&ishell.Cmd{
Name: "det",
Help: "determinate progress bar",
Func: func(c *ishell.Context) {
c.ProgressBar().Start()
for i := 0; i < 101; i++ {
c.ProgressBar().Suffix(fmt.Sprint(" ", i, "%"))
c.ProgressBar().Progress(i)
time.Sleep(time.Millisecond * 100)
}
c.ProgressBar().Stop()
},
})
// indeterminate
shell.AddCmd(&ishell.Cmd{
Name: "ind",
Help: "indeterminate progress bar",
Func: func(c *ishell.Context) {
c.ProgressBar().Indeterminate(true)
c.ProgressBar().Start()
time.Sleep(time.Second * 10)
c.ProgressBar().Stop()
},
})
}
分析一下上面的源碼
上面介紹了一些常用的命令究恤,下面我們直接看源碼:
shell.AddCmd(&ishell.Cmd{
Name: "det",
Help: "determinate progress bar",
Func: func(c *ishell.Context) {
c.ProgressBar().Start()
for i := 0; i < 101; i++ {
c.ProgressBar().Suffix(fmt.Sprint(" ", i, "%"))
c.ProgressBar().Progress(i)
time.Sleep(time.Millisecond * 100)
}
c.ProgressBar().Stop()
},
})
上面很多操作都是在 func(c *ishell.Context)
里面操作的
type Context struct {
contextValues
progressBar ProgressBar
err error
// Args is command arguments.
Args []string
// RawArgs is unprocessed command arguments.
RawArgs []string
// Cmd is the currently executing command. This is empty for NotFound and Interrupt.
Cmd Cmd
Actions
}
重要 內(nèi)容都在Actions中
// Actions are actions that can be performed by a shell.
type Actions interface {
// ReadLine reads a line from standard input.
ReadLine() string
// ReadLineErr is ReadLine but returns error as well
ReadLineErr() (string, error)
// ReadLineWithDefault reads a line from standard input with default value.
ReadLineWithDefault(string) string
// ReadPassword reads password from standard input without echoing the characters.
// Note that this only works as expected when the standard input is a terminal.
ReadPassword() string
// ReadPasswordErr is ReadPassword but returns error as well
ReadPasswordErr() (string, error)
// ReadMultiLinesFunc reads multiple lines from standard input. It passes each read line to
// f and stops reading when f returns false.
ReadMultiLinesFunc(f func(string) bool) string
// ReadMultiLines reads multiple lines from standard input. It stops reading when terminator
// is encountered at the end of the line. It returns the lines read including terminator.
// For more control, use ReadMultiLinesFunc.
ReadMultiLines(terminator string) string
// Println prints to output and ends with newline character.
Println(val ...interface{})
// Print prints to output.
Print(val ...interface{})
// Printf prints to output using string format.
Printf(format string, val ...interface{})
// ShowPaged shows a paged text that is scrollable.
// This leverages on "less" for unix and "more" for windows.
ShowPaged(text string) error
// ShowPagedReader shows a paged text that is scrollable, from a reader source.
// This leverages on "less" for unix and "more" for windows.
ShowPagedReader(r io.Reader) error
// MultiChoice presents options to the user.
// returns the index of the selection or -1 if nothing is
// selected.
// text is displayed before the options.
MultiChoice(options []string, text string) int
// Checklist is similar to MultiChoice but user can choose multiple variants using Space.
// init is initially selected options.
Checklist(options []string, text string, init []int) []int
// SetPrompt sets the prompt string. The string to be displayed before the cursor.
SetPrompt(prompt string)
// SetMultiPrompt sets the prompt string used for multiple lines. The string to be displayed before
// the cursor; starting from the second line of input.
SetMultiPrompt(prompt string)
// SetMultiChoicePrompt sets the prompt strings used for MultiChoice().
SetMultiChoicePrompt(prompt, spacer string)
// SetChecklistOptions sets the strings representing the options of Checklist().
// The generated string depends on SetMultiChoicePrompt() also.
SetChecklistOptions(open, selected string)
// ShowPrompt sets whether prompt should show when requesting input for ReadLine and ReadPassword.
// Defaults to true.
ShowPrompt(show bool)
// Cmds returns all the commands added to the shell.
Cmds() []*Cmd
// HelpText returns the computed help of top level commands.
HelpText() string
// ClearScreen clears the screen. Same behaviour as running 'clear' in unix terminal or 'cls' in windows cmd.
ClearScreen() error
// Stop stops the shell. This will stop the shell from auto reading inputs and calling
// registered functions. A stopped shell is only inactive but totally functional.
// Its functions can still be called and can be restarted.
Stop()
}
具體的用法說明俭令,有注釋。
如果需要深入部宿,就自己看吧抄腔。有什么問題,可以私信給我。
下面我展示一下demo