I need a one-liner from inside a go routine to change a user’s password in linux.
The command that WORKS from the command line:
echo 'pgc:password' | sudo chpasswd //"pgc" is the username and "password" // is the password I'm changing it to.
But this doesn’t work from my Go program. I have tried substituting other one-liner commands, such as: drm file.txt, touch file.txt, etc.
And those all work.
The Go program is in a package inside a big project, but I am now just attempting to run it directly from the command line (not used as a function, but a standalone .go file).
My Code:
//I have tried changing back and forth between the package that changesystempassword.go is in // and main, but that has no effect package main //one-liners DON'T WORK if package is the package this go file is in import ( "fmt" "os/exec" //"time" ) func main() { err := exec.Command("echo", "'pgc:password'", "|", "sudo", "chpasswd).Run() //time.sleep(time.Second) - tried adding a sleep so it would have time? if err != nil { fmt.Println("Password change unsuccessful" } else { fmt.Println("Password change successful") } }
The result when the program is run (./changesystempassword from the command line) is that the command line shows “Password change successful”. But guess what. It hasn’t changed. I have found a few similar examples online and on Stack Exchange, but I am using the solutions I found there and it isn’t working.
Advertisement
Answer
The documentation says “execute the named program with the given arguments”. There even is a specific paragraph:
Unlike the “system” library call from C and other languages, the os/exec package intentionally does not invoke the system shell and does not expand any glob patterns or handle other expansions, pipelines, or redirections typically done by shells.
So the code in the question executes echo
with the arguments 'pgc:password'
, |
, sudo
, and chpasswd
. This is successful as echo
can totally print those four strings.
The solution is to start chpasswd
directly and write to its standard input. This is a minimal example:
func main() { cmd := exec.Command("chpasswd") stdin, err := cmd.StdinPipe() io.WriteString(stdin, "pgc:password") }
I advise to adjust the code shown in the official example for safe code with error-checking.
You can also use sudo chpasswd
instead of chpasswd
. Keep in mind that sudo
will not be able to ask for a password in this scenario. One workaround is configuring sudoers with NOPASSWD where appropriate.