Exec cmd failed silently
huydinhle opened this issue · 14 comments
I'm trying to execute a onepassword cli command and it doesn't work even though exit code return 0
func main() {
cmd := "op item create --format=json --vault=<REDACTED> --category=\"API Credential\" --title=test credentials=secretivepassword notes=lala "
p := script.Exec(cmd)
//p.Wait()
num, err := p.Stdout()
if err != nil {
log.Fatal().Err(err).Msg("failed at executing")
}
log.Info().Int("num", int(num)).Msg("output")
}
If I add p.Wait() in there I will get error which is
{"level":"fatal","error":"io: read/write on closed pipe","time":"2022-12-05T22:23:03-08:00","message":"failed at executing"}
How can I debug or fix this?
If I copied the exact thing into my terminal, it would work correctly.
If I use the exec command, then it works properly
func main() {
out, errout, err := Shellout("op item create --format=json --vault=<REDACTED> --category=\"API Credential\" --title=test credentials=secretivepassword notes=lala ")
if err != nil {
log.Info().Err(err)
}
fmt.Println("--- stdout ---")
fmt.Println(out)
fmt.Println("--- stderr ---")
fmt.Println(errout)
}
const ShellToUse = "bash"
func Shellout(command string) (string, string, error) {
var stdout bytes.Buffer
var stderr bytes.Buffer
cmd := exec.Command(ShellToUse, "-c", command)
cmd.Stdout = &stdout
cmd.Stderr = &stderr
err := cmd.Run()
return stdout.String(), stderr.String(), err
}
p.Wait() waits for the command to finish, throws its output away, and then closes the pipe, which explains why you can't then call Stdout() on it—there's nothing left to write to stdout!
When you say "it doesn't work even though exit code return 0", what do you mean? What does the program print to stdout? If the op command prints an error message, you should see that.
@bitfield I tried adding p.Wait() to see if it has anything to do with the pipe not finishing.
However, if there is no p.Wait involved, script would just exit 0 with nothing on Stdout. 🤷 . If I send the exact cmd into the exec instead of script then it gives the correct output with 0 exit code.
I think you're running into #148 here—clearly the program is returning some error, but we're not seeing it. Let me check back with you here once that issue is fixed, and we'll see if that sheds any light on the problem.
Could you check your program against script v0.21.4 and see if it now works as expected? (Or at least, if it doesn't, that you can see any error reported by the op command.)
@bitfield This is very interesting. It might have something to do with onepassword cli. I tried this script version and still the same problem
package main
import (
"github.com/bitfield/script"
"github.com/rs/zerolog/log"
)
func main() {
cmd := `op item create`
p := script.Exec(cmd)
exitCode, err := p.Stdout()
if err != nil {
log.Fatal().Err(err).Msg("failed at executing")
}
log.Info().Int("num", int(exitCode)).Msg("output")
}
Output with go run main.go
{"level":"info","num":0,"time":"2022-12-07T08:42:16-08:00","message":"output"}
and If I run this with just my terminal this is what I got
❯ op item create
[ERROR] 2022/12/07 08:44:30 Provide the item category with '--category' flag
you can easily reproduce this by install onepassword cli
https://developer.1password.com/docs/cli/get-started/
On mac you do
brew install --cask 1password/tap/1password-cli
op --version
ping! @bitfield, Is there any update on this bug? Here is the minimal code to reproduce it, and the output using the op cli in the terminal vs using script package. Tried to debug it this for a while to see what's wrong but I cannot figure it out
minimal code to reproduce
package main
import (
"github.com/bitfield/script"
"github.com/rs/zerolog/log"
)
func main() {
cmd := `op item create`
p := script.Exec(cmd)
exitCode, err := p.Match("hello").Stdout()
if err != nil {
log.Fatal().Err(err).Msg("failed at executing")
}
log.Info().Int("num", int(exitCode)).Msg("output")
}
op cli output in terminal
❯ op item create
[ERROR] 2022/12/08 09:31:09 You are not currently signed in. Please run `op signin --help` for instructions
script package output
❯ go run main.go
{"level":"info","num":0,"time":"2022-12-08T09:31:38-08:00","message":"output"}
Notes: bitfield/script exit 0 as if there is no error
I don't have anything useful for you at the moment; I can point out a couple of problems with your program, though.
- The
p.Match("hello")stage will automatically filter out any output lines fromopthat don't contain"hello". That's probably not what you want if you want to see any error message printed by the command. - The first value returned by
Stdoutis not the exit status, but the number of bytes successfully written to the output. You can get the exit status of the lastExeccommand run by the pipe withExitStatus:
func main() {
cmd := `op whoami`
p := script.Exec(cmd)
_, err := p.Stdout()
if err != nil {
log.Fatal(err)
}
fmt.Println(p.ExitStatus())
}For me, this prints:
[ERROR] 2022/12/08 18:03:38 no account found for filter
2022/12/08 18:03:38 exit status 1
exit status 1
... which is roughly what I'd expect. I can't try op item create because it prompts me to link to an account, but I don't have one. But see what the above program produces for you when you run op item create.
@bitfield my bad on this. you don't need to do the match, and you do not need to login to reproduce it. The whole point is to not login and run this exact program and see script exit 0 instead of erroring out
package main
import (
"github.com/bitfield/script"
"github.com/rs/zerolog/log"
)
func main() {
cmd := `op item create`
exitCode, err := script.Exec(cmd).Stdout()
if err != nil {
log.Fatal().Err(err).Msg("failed at executing")
}
log.Info().Int("num", int(exitCode)).Msg("output")
}
{"level":"info","num":0,"time":"2022-12-08T10:11:32-08:00","message":"output"}
@bitfield script should have been erroring out like the op command did like this
❯ op item create
[ERROR] 2022/12/08 10:14:09 You are not currently signed in. Please run `op signin --help` for instructions
Yes, it looks as though the op item create command is returning zero exit status and producing no output in this case, doesn’t it? I have no idea why that would be—is the source for that tool available?
Evidently op whoami does return a non-zero status and print its error, as expected, and script handles that correctly. So if this is a bug in script itself, I’m puzzled about what it could be.
@bitfield true, true . yeah you can close this if you want. Pretty sure this is a op problem. Thank you for answering all the questions
I'll leave it open for now just in case anyone else has problems with automating op and comes here to find out why! This is something I'd raise with the OnePassword support team, since presumably their tool is meant to work correctly in this situation, but doesn't.