// ExecShell 在指定目录执行一个shell指令,并获得其输出
func ExecShell(dir string, commandName string, arg ...string) (msg string, err error) {
cmd := exec.Command(commandName, arg...)
var outbuf, errbuf bytes.Buffer
cmd.Stdout = &outbuf
cmd.Stderr = &errbuf
cmd.Dir = dir
if err = cmd.Run(); err != nil {
msg = fmt.Sprintf("shell command: %s %v is failed: %s, caused: %s", cmd.Path, cmd.Args, err, errbuf.String())
return msg, err
}
msg = fmt.Sprintf("Execute shell %s %v finished with output: %s", cmd.Path, cmd.Args, outbuf.String())
return msg, nil
}
// ExecSubProcessBackground 在指定目录后台执行一个shell命令,用于启动一个独立的子进程,即使本进程退出,子进程仍然可以继续运行
func ExecSubProcessBackground(dir string, command string) (msg string, err error) {
// 像需要后台运行这种复杂的操作,需要用 sh 的 -c 参数,来一次性传一个复杂的指令
// -c 后面只能接收1个参数,如果传了多个,多余的命令会被直接舍弃
cmd := exec.Command("/bin/bash", "-c", fmt.Sprintf("nohup %s >out.log 2>&1 & echo $! > command.pid", command))
var outbuf, errbuf bytes.Buffer
cmd.Stdout = &outbuf
cmd.Stderr = &errbuf
cmd.Dir = dir
if err = cmd.Run(); err != nil {
msg = fmt.Sprintf("shell command: %s %v is failed: %s, caused: %s", cmd.Path, cmd.Args, err, errbuf.String())
return msg, err
}
msg = fmt.Sprintf("Execute shell %s %v finished with output: %s", cmd.Path, cmd.Args, outbuf.String())
return msg, nil
}
func TestInteractiveShell(t *testing.T) {
in := bytes.NewBuffer(nil)
cmd := exec.Command("redis-cli")
// cmd := exec.Command("bash", "-c", "~/xxx/xxx.sh xxx xxx")
cmd.Stdin = in
cmd.Stdout = os.Stdout
go func() {
in.WriteString("keys *\n")
in.WriteString("get stock:0\n")
}()
err := cmd.Run()
assert.Nil(t, err)
}
cmd.Process.Kill() // cmd.Start()之后如果只调用kill会导致子进程成为僵尸进程(通过ps指令能查到 process_name <defunct> 进程)
if err := cmd.Process.Kill(); err != nil {
return err
}
go func() {
if err := cmd.Wait(); err != nil {
creator.Logger.Warn(err)
}
}() // 异步等待