golang os/exec调用shell脚本卡住问题排

代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package main

import (
"fmt"
"os/exec"
)


func main() {
cmd := exec.Command("/bin/sh", "/tmp/1.sh")
out, err := cmd.Output()

if err != nil {
fmt.Println(err)
} else {
fmt.Println(string(out))
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# cat /tmp/1.sh
date
#./t (不会卡住)
Tue Mar 15 17:59:29 CST 2016
# vim /tmp/1.sh
# cat /tmp/1.sh
/etc/init.d/salt-minion restart
# strace ./t (这里一直卡住不退出)
...
read(4, "Stopping salt-minion daemon: ", 512) = 29
futex(0x58bac0, FUTEX_WAKE, 1) = 1
read(4, "[", 1507) = 1
futex(0x58bac0, FUTEX_WAKE, 1) = 1
read(4, " OK ]\r", 1506) = 8
read(4, "\n", 1498) = 1
futex(0x58bac0, FUTEX_WAKE, 1) = 1
read(4, "Starting salt-minion daemon: ", 1497) = 29
read(4, "[", 1468) = 1
futex(0x58bac0, FUTEX_WAKE, 1) = 1
futex(0x58ba40, FUTEX_WAKE, 1) = 1
read(4, " OK ]\r\n", 1467) = 9
read(4, 0xc20807a04e, 1458) = ? ERESTARTSYS (To be restarted)
--- SIGWINCH (Window changed) @ 0 (0) ---
rt_sigreturn(0x1c) = 0
read(4,

执行/etc/init.d/salt-minion restart后,脚本一直不退出。
困扰了我一下午,一直找不到原因,后来找组内的go浪大神帮忙看了下,问题终于解决了。

原因

执行/etc/init.d/salt-minion restart后,会启动一个salt-minion的子进程.cmd.Output执行后会去调一个Wait的方法,Wait方法会等待所有进程都执行完后才会退出。这样就形成了死循环,导致一直不退出。

解决

直接调用exec的Start方法,绕过Wait方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package main

import (
"bytes"
"fmt"
"os/exec"
)

func main() {
var b bytes.Buffer
command := exec.Command("/bin/sh", "/tmp/1.sh")
command.Stdout = &b
command.Stderr = &b
err := command.Start()
if err != nil {
fmt.Println(b.String(), err)
}
_, err = command.Process.Wait() //等待主进程执行完成
fmt.Println(b.String(), err)
}