when I execute an go exec command, it stucks, I don't know why?
the Go code:
func main() {
cmd := exec.Command("/bin/bash", "test.sh")
_, err := cmd.Output()
//err := cmd.Run()
if err != nil {
fmt.Println(err)
} else {
fmt.Println("out")
}
}
as show in the code, if use Run(), it's OK.
the test.sh:
#!/bin/bash
./sleep.sh &
it call another shell script, run sleep.sh in background
the sleep.sh:
#!/bin/bash
while true
do
echo hello >> test.txt
sleep 30
done
it never quit, always run in the background.
you can see actually it will not output anything to stdout, but Output() will Stuck, Run() can just fine. the source code of Go show Output() will call Run(). so why?
some GDB info when the program stuck:
(gdb) info goroutines
1 waiting runtime.gopark
2 waiting runtime.gopark
3 waiting runtime.gopark
4 waiting runtime.gopark
* 5 syscall syscall.Syscall
(gdb) goroutine 5 bt
#0 syscall.Syscall () at /usr/local/go/src/syscall/asm_linux_amd64.s:19
#1 0x00000000004acf2f in syscall.read (fd=4, p= []uint8 = {...}, n=4254696, err=...) at /usr/local/go/src/syscall/zsyscall_linux_amd64.go:783
#2 0x00000000004ac73d in syscall.Read (fd=4, p= []uint8 = {...}, n=859530501352, err=...) at /usr/local/go/src/syscall/syscall_unix.go:160
#3 0x0000000000487703 in os.(*File).read (f=0xc82002c030, b= []uint8 = {...}, n=859530919936, err=...) at /usr/local/go/src/os/file_unix.go:211
#4 0x0000000000484f3a in os.(*File).Read (f=0xc82002c030, b= []uint8 = {...}, n=0, err=...) at /usr/local/go/src/os/file.go:95
#5 0x00000000004a5c6f in bytes.(*Buffer).ReadFrom (b=0xc8200140e0, r=..., n=0, err=...) at /usr/local/go/src/bytes/buffer.go:173
#6 0x0000000000482160 in io.copyBuffer (dst=..., src=..., buf= []uint8, written=0, err=...) at /usr/local/go/src/io/io.go:375
#7 0x0000000000481fa4 in io.Copy (dst=..., src=..., written=0, err=...) at /usr/local/go/src/io/io.go:351
#8 0x000000000047262b in os/exec.(*Cmd).writerDescriptor.func1 (~r0=...) at /usr/local/go/src/os/exec/exec.go:232
#9 0x00000000004726cd in os/exec.(*Cmd).Start.func1 (c=0xc820088000, fn={void (error *)} 0xc820029fb8) at /usr/local/go/src/os/exec/exec.go:340
#10 0x0000000000456241 in runtime.goexit () at /usr/local/go/src/runtime/asm_amd64.s:1721
#11 0x000000c820088000 in ?? ()
#12 0x000000c82000c140 in ?? ()
#13 0x0000000000000000 in ?? ()
What you are seeing is normal behaviour, but it's a little unusual. What happens is your program forks /bin/bash, which forks test.sh, which forks sleep.sh. Now you are waiting on Output, which means the stdout from the command you executed, /bin/bash, which has forked test.sh, then returned.
So, why does this behave differently when you just call cmd.Run ? Because your program forks /bin/bash, which then forks test.sh, and returns. Your program is not waiting to read the output of the program, it's just looking for the exit status of /bin/bash, which returns immediately.
Here are some exercises: