前言
好久没写东西了,所以翻出老东西继续写点水水的玩意儿QAQ
核心原理这个包github.com/howeyc/gopass的代码实现已经写的很清晰了,其实就是正确使用golang.org/x/crypto/ssh/terminal中MakeRaw功能即可。
实现
“请按任意键继续”是windows bat中很常见的一句话。
这里关键点在于任意键,Go中用fmt.Scan等函数程序处理输入都是在按下回车键后才进入我们的处理逻辑的,所以这里就要用到terminal.MakeRaw函数,通过传入stdin的文件描述符,亲自处理每个传进来的字节。
同样的,因为用MakeRaw能够处理每个传进来的字节,所以我们不仅能实现按任意键继续,也能实现按指定的字符后继续
直接贴代码吧,加上注释,代码扔在了https://github.com/Starainrt/stario:
// StopUntil 按下指定的字符函数才会返回,如果不是指定的字符,返回error(repear=false)
// 或阻塞住直到按下指定的字符(repeat=true)
// hint提示文本,trigger指定的字符,repeat是否允许多次重试
func StopUntil(hint string, trigger string, repeat bool) error {
pressLen := len([]rune(trigger))
if trigger == "" {
pressLen = 1
}
//这里获取stdin标准输入的文件描述符
fd := int(os.Stdin.Fd())
if hint != "" {
//这里打印自定义提示,如:“请按任意键继续”
fmt.Print(hint)
}
// 进入Raw模式
state, err := terminal.MakeRaw(fd)
if err != nil {
return err
}
//defer退出Raw模式
defer terminal.Restore(fd, state)
inputReader := bufio.NewReader(os.Stdin)
//ioBuf := make([]byte, pressLen)
i := 0
for {
//这里用了Rune,是考虑到中文字符
b, _, err := inputReader.ReadRune()
if err != nil {
return err
}
//指定的字符为空,按下任意键直接返回
if trigger == "" {
break
}
if b == []rune(trigger)[i] {
i++
if i == pressLen {
break
}
continue
}
i = 0
if hint != "" && repeat {
fmt.Print("\r\n")
fmt.Print(hint)
}
}
return nil
}
测试代码:
package main
import (
"b612.me/stario"
"fmt"
)
func main() {
if err := stario.StopUntil("请按任意键继续", "", false); err != nil {
panic(err)
}
fmt.Println("\ncontinued")
if err := stario.StopUntil("请按b612继续", "b612", true); err != nil {
panic(err)
}
fmt.Println("\nbye bye")
}
效果:
密码掩码的实现原理一样,差别就是读取到输入的时候屏幕上打印*或者其他自定义字符,实现可参考gopass包
直接的简单的实现可参考下面的代码,代码同样扔在了https://github.com/Starainrt/stario:
type InputMsg struct {
msg string
err error
}
func passwd(hint string, defaultVal string, mask string) InputMsg {
var ioBuf []rune
if hint != "" {
fmt.Print(hint)
}
if strings.Index(hint, "\n") >= 0 {
hint = strings.TrimSpace(hint[strings.LastIndex(hint, "\n"):])
}
fd := int(os.Stdin.Fd())
state, err := terminal.MakeRaw(fd)
if err != nil {
return InputMsg{"", err}
}
defer fmt.Println()
defer terminal.Restore(fd, state)
inputReader := bufio.NewReader(os.Stdin)
for {
b, _, err := inputReader.ReadRune()
if err != nil {
return InputMsg{"", err}
}
if b == 0x0d {
strValue := strings.TrimSpace(string(ioBuf))
if len(strValue) == 0 {
strValue = defaultVal
}
return InputMsg{strValue, nil}
}
if b == 0x08 || b == 0x7F {
if len(ioBuf) > 0 {
ioBuf = ioBuf[:len(ioBuf)-1]
}
fmt.Print("\r")
for i := 0; i < len(ioBuf)+2+len(hint); i++ {
fmt.Print(" ")
}
} else {
ioBuf = append(ioBuf, b)
}
fmt.Print("\r")
if hint != "" {
fmt.Print(hint)
}
for i := 0; i < len(ioBuf); i++ {
fmt.Print(mask)
}
}
}
测试代码:
package main
import (
"b612.me/stario"
"fmt"
)
func main() {
//设置掩码为*
fmt.Println("输入的密码为:",
stario.PasswdWithMask("请输入密码:", "", "*").MustString())
//不设置掩码,输入密码时不显示
fmt.Println("输入的密码为:",
stario.PasswdWithMask("请输入密码:", "", "").MustString())
//设置掩码为@
fmt.Println("输入的密码为:",
stario.PasswdWithMask("请输入密码:", "", "@").MustString())
}
March 14th, 2023 at 08:15 pm
膜拜大佬