golang exec输出()卡住了,但Run()没问题

时间:2016-09-06 01:53:31

标签: go

当我执行go exec命令时,它卡住了,我不知道为什么?
Go代码:

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")
    }
}

如代码所示,如​​果使用Run(),则没关系。

test.sh:

#!/bin/bash
./sleep.sh &

它调用另一个shell脚本,在后台运行sleep.sh

sleep.sh:

#!/bin/bash
while true
do
  echo hello >> test.txt
  sleep 30
done

它永远不会退出,总是在后台运行。

你可以看到它实际上不会向stdout输出任何东西,但是Output()会Stuck,Run()就可以了。 Go show Output()的源代码将调用Run()。所以为什么?

程序停滞时的一些GDB信息:

(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 ?? ()

1 个答案:

答案 0 :(得分:1)

您所看到的是正常行为,但它有点不寻常。你的程序forks / bin / bash会发生什么,它会分叉test.sh,它会分配sleep.sh。现在你正在等待输出,这意味着你执行的命令的标准输出/ bin / bash,它已经分叉了test.sh,然后返回。

那么,当您调用cmd.Run时,为什么这会有不同的行为?因为你的程序分叉/ bin / bash,然后分叉test.sh,然后返回。你的程序没有等待读取程序的输出,它只是查找/ bin / bash的退出状态,它会立即返回。

以下是一些练习:

  1. 修改代码示例以使用cmd.StdoutPipe
  2. 观察等待cmd.StdoutPipe()之间行为的变化.Read()返回io.EOF(ioutils.ReadAll在这里很有用)和另一个goroutine等待cmd.Run()。一定不要让你的主goroutine出口直到从管道读出的另一个退出。