英文:
getpasswd functionality in Go?
问题
情况:
我想从stdin
控制台获取一个密码输入,而不显示用户输入的内容。在Go语言中是否有类似于getpasswd
功能的东西?
我尝试过的:
我尝试使用syscall.Read
,但它会显示用户输入的内容。
英文:
Situation:
I want to get a password entry from the stdin
console - without echoing what the user types. Is there something comparable to getpasswd
functionality in Go?
What I tried:
I tried using syscall.Read
, but it echoes what is typed.
答案1
得分: 131
以下是完成此任务的最佳方法之一。
首先通过go get golang.org/x/term
获取term
包。
package main
import (
"bufio"
"fmt"
"os"
"strings"
"syscall"
"golang.org/x/term"
)
func main() {
username, password, _ := credentials()
fmt.Printf("用户名: %s, 密码: %s\n", username, password)
}
func credentials() (string, string, error) {
reader := bufio.NewReader(os.Stdin)
fmt.Print("输入用户名: ")
username, err := reader.ReadString('\n')
if err != nil {
return "", "", err
}
fmt.Print("输入密码: ")
bytePassword, err := term.ReadPassword(int(syscall.Stdin))
if err != nil {
return "", "", err
}
password := string(bytePassword)
return strings.TrimSpace(username), strings.TrimSpace(password), nil
}
http://play.golang.org/p/l-9IP1mrhA
英文:
The following is one of best ways to get it done.
First get term
package by go get golang.org/x/term
package main
import (
"bufio"
"fmt"
"os"
"strings"
"syscall"
"golang.org/x/term"
)
func main() {
username, password, _ := credentials()
fmt.Printf("Username: %s, Password: %s\n", username, password)
}
func credentials() (string, string, error) {
reader := bufio.NewReader(os.Stdin)
fmt.Print("Enter Username: ")
username, err := reader.ReadString('\n')
if err != nil {
return "", "", err
}
fmt.Print("Enter Password: ")
bytePassword, err := term.ReadPassword(int(syscall.Stdin))
if err != nil {
return "", "", err
}
password := string(bytePassword)
return strings.TrimSpace(username), strings.TrimSpace(password), nil
}
答案2
得分: 29
刚刚在 #go-nuts 邮件列表中看到一封邮件。有人写了一个相当简单的 Go 包供使用。你可以在这里找到它:https://github.com/howeyc/gopass
它的代码如下:
package main
import "fmt"
import "github.com/howeyc/gopass"
func main() {
fmt.Printf("密码: ")
pass := gopass.GetPasswd()
// 使用 pass 做一些操作
}
英文:
Just saw a mail in #go-nuts maillist. There is someone who wrote quite a simple go package to be used. You can find it here: https://github.com/howeyc/gopass
It something like that:
package main
import "fmt"
import "github.com/howeyc/gopass"
func main() {
fmt.Printf("Password: ")
pass := gopass.GetPasswd()
// Do something with pass
}
答案3
得分: 24
自Go v1.11起,有一个官方包**golang.org/x/term
**取代了已弃用的crypto/ssh/terminal
。其中包含了函数term.ReadPassword
。
示例用法:
package main
import (
"fmt"
"os"
"syscall"
"golang.org/x/term"
)
func main() {
fmt.Print("密码: ")
bytepw, err := term.ReadPassword(int(syscall.Stdin))
if err != nil {
os.Exit(1)
}
pass := string(bytepw)
fmt.Printf("\n你输入的是: %q\n", pass)
}
英文:
Since Go ~v1.11 there is an official package golang.org/x/term
which replaces the deprecated crypto/ssh/terminal
. It has, among other things, the function term.ReadPassword
.
Example usage:
package main
import (
"fmt"
"os"
"syscall"
"golang.org/x/term"
)
func main() {
fmt.Print("Password: ")
bytepw, err := term.ReadPassword(int(syscall.Stdin))
if err != nil {
os.Exit(1)
}
pass := string(bytepw)
fmt.Printf("\nYou've entered: %q\n", pass)
}
答案4
得分: 11
我有一个类似的用例,下面的代码片段对我很有效。如果你还在困扰,可以随意尝试一下。
import (
"fmt"
"golang.org/x/crypto/ssh/terminal"
)
func main() {
fmt.Printf("现在,请输入密码(必填):")
password, _ := terminal.ReadPassword(0)
fmt.Printf("密码是:%s", password)
}
当然,你需要事先使用go get
安装terminal包。
英文:
I had a similar usecase and the following code snippet works well for me. Feel free to try this if you are still stuck here.
import (
"fmt"
"golang.org/x/crypto/ssh/terminal"
)
func main() {
fmt.Printf("Now, please type in the password (mandatory): ")
password, _ := terminal.ReadPassword(0)
fmt.Printf("Password is : %s", password)
}
Of course, you need to install terminal package using go get
beforehand.
答案5
得分: 7
你可以通过执行stty -echo
来关闭回显,然后在读取密码后执行stty echo
来重新打开回显。
英文:
you can do this by execing stty -echo
to turn off echo and then stty echo
after reading in the password to turn it back on
答案6
得分: 5
这是我使用Go1.6.2开发的一个解决方案,你可能会觉得有用。
它只使用了以下标准包:bufio
,fmt
,os
,strings
和syscall
。具体来说,它使用了syscall.ForkExec()
和syscall.Wait4()
来调用stty来禁用/启用终端回显。
我在Linux和BSD(Mac)上进行了测试。它在Windows上不起作用。
// getPassword - 提示输入密码。使用stty禁用回显。
import ( "bufio"; "fmt"; "os"; "strings"; "syscall" )
func getPassword(prompt string) string {
fmt.Print(prompt)
// 用于两个stty调用的公共设置和变量。
attrs := syscall.ProcAttr{
Dir: "",
Env: []string{},
Files: []uintptr{os.Stdin.Fd(), os.Stdout.Fd(), os.Stderr.Fd()},
Sys: nil}
var ws syscall.WaitStatus
// 禁用回显。
pid, err := syscall.ForkExec(
"/bin/stty",
[]string{"stty", "-echo"},
&attrs)
if err != nil {
panic(err)
}
// 等待stty进程完成。
_, err = syscall.Wait4(pid, &ws, 0, nil)
if err != nil {
panic(err)
}
// 回显已禁用,现在获取数据。
reader := bufio.NewReader(os.Stdin)
text, err := reader.ReadString('\n')
if err != nil {
panic(err)
}
// 启用回显。
pid, err = syscall.ForkExec(
"/bin/stty",
[]string{"stty", "echo"},
&attrs)
if err != nil {
panic(err)
}
// 等待stty进程完成。
_, err = syscall.Wait4(pid, &ws, 0, nil)
if err != nil {
panic(err)
}
return strings.TrimSpace(text)
}
英文:
Here is a solution that I developed using Go1.6.2 that you might find useful.
It only uses the following standard packages: bufio
, fmt
, os
, strings
and syscall
. More specifically, it uses syscall.ForkExec()
and syscall.Wait4()
to invoke stty to disable/enable terminal echo.
I have tested it on Linux and BSD (Mac). It will not work on windows.
// getPassword - Prompt for password. Use stty to disable echoing.
import ( "bufio"; "fmt"; "os"; "strings"; "syscall" )
func getPassword(prompt string) string {
fmt.Print(prompt)
// Common settings and variables for both stty calls.
attrs := syscall.ProcAttr{
Dir: "",
Env: []string{},
Files: []uintptr{os.Stdin.Fd(), os.Stdout.Fd(), os.Stderr.Fd()},
Sys: nil}
var ws syscall.WaitStatus
// Disable echoing.
pid, err := syscall.ForkExec(
"/bin/stty",
[]string{"stty", "-echo"},
&attrs)
if err != nil {
panic(err)
}
// Wait for the stty process to complete.
_, err = syscall.Wait4(pid, &ws, 0, nil)
if err != nil {
panic(err)
}
// Echo is disabled, now grab the data.
reader := bufio.NewReader(os.Stdin)
text, err := reader.ReadString('\n')
if err != nil {
panic(err)
}
// Re-enable echo.
pid, err = syscall.ForkExec(
"/bin/stty",
[]string{"stty", "echo"},
&attrs)
if err != nil {
panic(err)
}
// Wait for the stty process to complete.
_, err = syscall.Wait4(pid, &ws, 0, nil)
if err != nil {
panic(err)
}
return strings.TrimSpace(text)
}
答案7
得分: 2
需要通过Go的ForkExec()函数来启动stty:
package main
import (
os "os"
bufio "bufio"
fmt "fmt"
str "strings"
)
func main() {
fmt.Println();
if passwd, err := Getpasswd("输入密码: "); err == nil {
fmt.Printf("\n\n密码: '%s'\n",passwd)
}
}
func Getpasswd(prompt string) (passwd string, err os.Error) {
fmt.Print(prompt);
const stty_arg0 = "/bin/stty";
stty_argv_e_off := []string{"stty","-echo"};
stty_argv_e_on := []string{"stty","echo"};
const exec_cwdir = "";
fd := []*os.File{os.Stdin,os.Stdout,os.Stderr};
pid, err := os.ForkExec(stty_arg0,stty_argv_e_off,nil,exec_cwdir,fd);
if err != nil {
return passwd, os.NewError(fmt.Sprintf("关闭密码输入时的控制台回显失败:\n\t%s",err))
}
rd := bufio.NewReader(os.Stdin);
os.Wait(pid,0);
line, err := rd.ReadString('\n');
if err == nil {
passwd = str.TrimSpace(line)
} else {
err = os.NewError(fmt.Sprintf("密码输入过程中出错: %s",err))
}
pid, e := os.ForkExec(stty_arg0,stty_argv_e_on,nil,exec_cwdir,fd);
if e == nil {
os.Wait(pid,0)
} else if err == nil {
err = os.NewError(fmt.Sprintf("开启密码输入后的控制台回显失败:\n\t%s",e))
}
return passwd, err
}
英文:
Required launching stty via Go ForkExec() function:
package main
import (
os "os"
bufio "bufio"
fmt "fmt"
str "strings"
)
func main() {
fmt.Println();
if passwd, err := Getpasswd("Enter password: "); err == nil {
fmt.Printf("\n\nPassword: '%s'\n",passwd)
}
}
func Getpasswd(prompt string) (passwd string, err os.Error) {
fmt.Print(prompt);
const stty_arg0 = "/bin/stty";
stty_argv_e_off := []string{"stty","-echo"};
stty_argv_e_on := []string{"stty","echo"};
const exec_cwdir = "";
fd := []*os.File{os.Stdin,os.Stdout,os.Stderr};
pid, err := os.ForkExec(stty_arg0,stty_argv_e_off,nil,exec_cwdir,fd);
if err != nil {
return passwd, os.NewError(fmt.Sprintf("Failed turning off console echo for password entry:\n\t%s",err))
}
rd := bufio.NewReader(os.Stdin);
os.Wait(pid,0);
line, err := rd.ReadString('\n');
if err == nil {
passwd = str.TrimSpace(line)
} else {
err = os.NewError(fmt.Sprintf("Failed during password entry: %s",err))
}
pid, e := os.ForkExec(stty_arg0,stty_argv_e_on,nil,exec_cwdir,fd);
if e == nil {
os.Wait(pid,0)
} else if err == nil {
err = os.NewError(fmt.Sprintf("Failed turning on console echo post password entry:\n\t%s",e))
}
return passwd, err
}
答案8
得分: 2
这是一个针对Linux特定的版本:
func terminalEcho(show bool) {
// 启用或禁用终端输入的回显。这对于用户输入密码时特别有用。
// 调用 terminalEcho(true) 打开回显(正常模式)
// 调用 terminalEcho(false) 隐藏终端输入。
var termios = &syscall.Termios{}
var fd = os.Stdout.Fd()
if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd,
syscall.TCGETS, uintptr(unsafe.Pointer(termios))); err != 0 {
return
}
if show {
termios.Lflag |= syscall.ECHO
} else {
termios.Lflag &=^ syscall.ECHO
}
if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd,
uintptr(syscall.TCSETS),
uintptr(unsafe.Pointer(termios))); err != 0 {
return
}
}
使用方法如下:
fmt.Print("password: ")
terminalEcho(false)
var pw string
fmt.Scanln(&pw)
terminalEcho(true)
fmt.Println("")
TCGETS系统调用是特定于Linux的。OSX和Windows有不同的系统调用值。
英文:
Here is a version specific to Linux:
func terminalEcho(show bool) {
// Enable or disable echoing terminal input. This is useful specifically for
// when users enter passwords.
// calling terminalEcho(true) turns on echoing (normal mode)
// calling terminalEcho(false) hides terminal input.
var termios = &syscall.Termios{}
var fd = os.Stdout.Fd()
if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd,
syscall.TCGETS, uintptr(unsafe.Pointer(termios))); err != 0 {
return
}
if show {
termios.Lflag |= syscall.ECHO
} else {
termios.Lflag &^= syscall.ECHO
}
if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd,
uintptr(syscall.TCSETS),
uintptr(unsafe.Pointer(termios))); err != 0 {
return
}
}
So to use it:
fmt.Print("password: ")
terminalEcho(false)
var pw string
fmt.Scanln(&pw)
terminalEcho(true)
fmt.Println("")
It's the TCGETS syscall that is linux specific. There are different syscall values for OSX and Windows.
答案9
得分: 1
您还可以使用https://github.com/peterh/liner软件包的PasswordPrompt
函数。
英文:
You could also use PasswordPrompt
function of https://github.com/peterh/liner package.
答案10
得分: 0
> 在输入之前关闭回显,在输入之后打开回显。
在Unix上,你可以找到上面显示的方法来完成它,但在Windows上很困难。
你可以通过使用Windows的kernel32.dll
中的SetConsoleMode
方法来实现,参考C: How to disable echo in windows console?中的被接受的答案。
func GetPassword(prompt string) (err error, text string) {
var modeOn, modeOff uint32
stdin := syscall.Handle(os.Stdin.Fd())
err = syscall.GetConsoleMode(stdin, &modeOn)
if err != nil {
return
}
modeOff = modeOn &^ 0x0004
proc := syscall.MustLoadDLL("kernel32").MustFindProc("SetConsoleMode")
fmt.Print(prompt)
_, _, _ = proc.Call(uintptr(stdin), uintptr(modeOff))
_, err = fmt.Scanln(&text)
if err != nil {
return
}
_, _, _ = proc.Call(uintptr(stdin), uintptr(modeOn))
fmt.Println()
return nil, strings.TrimSpace(text)
}
英文:
> Turning off echo before typing and turning on to turn it back on after typing.
Without third library, you can find ways to do with it on unix shown above. But it's difficult on Windows.
You can achieve it by method SetConsoleMode
with windows kernel32.dll
referring to the accepted answer from C: How to disable echo in windows console?
func GetPassword(prompt string) (err error, text string) {
var modeOn, modeOff uint32
stdin := syscall.Handle(os.Stdin.Fd())
err = syscall.GetConsoleMode(stdin, &modeOn)
if err != nil {
return
}
modeOff = modeOn &^ 0x0004
proc := syscall.MustLoadDLL("kernel32").MustFindProc("SetConsoleMode")
fmt.Print(prompt)
_, _, _ = proc.Call(uintptr(stdin), uintptr(modeOff))
_, err = fmt.Scanln(&text)
if err != nil {
return
}
_, _, _ = proc.Call(uintptr(stdin), uintptr(modeOn))
fmt.Println()
return nil, strings.TrimSpace(text)
}
答案11
得分: -3
你可以通过os.File对象的Read方法(或os.Stdin变量)来实现所需的行为。以下示例程序将读取一行文本(通过按下回车键终止),但在fmt.Printf调用之前不会回显。
package main
import "fmt"
import "os"
func main() {
var input []byte = make([]byte, 100)
os.Stdin.Read(input)
fmt.Printf("%s", input)
}
如果你想要更高级的行为,可能需要使用Go的C包装工具,并为低级API调用创建一些包装器。
英文:
You can get the behavior you want with the Read method from the os.File object (or the os.Stdin variable). The following sample program will read a line of text (terminated with by pressing the return key) but won't echo it until the fmt.Printf call.
package main
import "fmt"
import "os"
func main() {
var input []byte = make( []byte, 100 );
os.Stdin.Read( input );
fmt.Printf( "%s", input );
}
If you want more advanced behavior, you're probably going to have to use the Go C-wrapper utilities and create some wrappers for low-level api calls.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论