在这个最小的工作示例中,我尝试执行以下操作:
这是源代码:
package main
import (
"encoding/json"
"fmt"
"io"
"log"
"os"
"syscall"
"golang.org/x/crypto/ssh/terminal"
)
const correctPassword = "secret"
func main() {
args := os.Args[1:]
var passwd string
for {
passwd = promptPassword()
if passwd == correctPassword {
log.Println("Correct password! Begin processing...")
break
}
log.Println("Incorrect password!")
}
if len(args) == 0 { // Read from stdin
log.Println("Reading from stdin")
dec := json.NewDecoder(os.Stdin)
for {
var v interface{}
if err := dec.Decode(&v); err == io.EOF {
break
} else if err != nil {
log.Fatal(err)
}
log.Printf("%#v", v)
}
}
for _, fileName := range args {
log.Println("Reading from", fileName)
f, err := os.Open(fileName)
if err != nil {
log.Println(err)
continue
}
defer f.Close()
dec := json.NewDecoder(f)
for {
var v interface{}
if err := dec.Decode(&v); err == io.EOF {
break
} else if err != nil {
log.Fatal(err)
}
log.Printf("%#v", v)
}
}
}
func promptPassword() (passwd string) {
for {
fmt.Fprintln(os.Stderr, "Enter password:")
b, _ := terminal.ReadPassword(int(syscall.Stdin))
passwd = string(b)
if passwd != "" {
break
}
}
return passwd
}
除了通过管道传输或重定向已准备好的数据(例如go run main.go < mydata.json
或echo 42 | go run main.go
等)之外,一切正常。
当我通过管道将某些数据重定向到程序时,数据将由密码提示而不是JSON解码器部分处理。有什么方法可以在第一时间提示您输入密码,并且仅在处理输入数据之后?
我试图detect if there's any data in STDIN读取并存储在一些临时字节片中,但是我找不到如何关闭/截断STDIN的方法,因此它不会读取两次数据。
答案 0 :(得分:1)
无需对程序进行任何更改,您可以在json之前的stdin中包含密码,例如(bash):{echo pass; cat data.json; } | goprog
或cat pass.txt data.json | goprog
有关更好的密码传递方法(例如环境或文件描述符),请查看sshpass
:https://linux.die.net/man/1/sshpass
您还可以缓冲所有stdin,并稍后(通过io.Reader)重用其内容
重新设计您的应用程序逻辑,使其功能可以接受io.Reader
作为要解组的数据源。
在main()
中,如果命令行上没有文件参数,则将os.Stdin
传递给所提到的函数,否则(尝试)打开文件并将其传递给解组函数。
注意:要决定是否打印提示,可以使用类似isatty
的函数,该函数告诉stdin是否是交互式的:https://github.com/mattn/go-isatty
答案 1 :(得分:0)
您将无法执行此操作,因为外壳一旦完成写内容就将关闭stdin文件描述符。
一旦stdin关闭,您将无法重新打开它,这由外壳程序控制。
检查此程序,以测试此行为
package main
import (
"fmt"
"io"
"os"
)
func main() {
io.Copy(os.Stdout, os.Stdin)
fmt.Println("done")
some := make([]byte, 100)
_, err := os.Stdin.Read(some)
fmt.Println(err)
fmt.Println(string(some))
}
输出将为
$ echo "some" | go run main.go
some
done
EOF