Created
November 22, 2021 10:46
-
-
Save teocci/3f063654470287748081253822234e47 to your computer and use it in GitHub Desktop.
golang exec run get pid
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"bytes" | |
"context" | |
"fmt" | |
"log" | |
"os" | |
"os/exec" | |
"sync" | |
"syscall" | |
"time" | |
"github.com/pkg/errors" | |
) | |
func main() { | |
ctx, cancel := context.WithCancel(context.Background()) | |
c := commandContext(ctx, "ls", "-al") | |
var wg sync.WaitGroup | |
wg.Add(1) | |
go func() { | |
defer wg.Done() | |
out, err := c.CombinedOutput() | |
if err != nil { | |
log.Printf("err: %v", err) | |
} else { | |
log.Printf("output: %s", out) | |
} | |
}() | |
time.Sleep(500 * time.Millisecond) | |
pid := fmt.Sprint(c.Cmd.Process.Pid) | |
log.Printf("pid:%s\n", pid) | |
cancel() | |
wg.Wait() | |
} | |
type cmd struct { | |
// ctx is provided by the caller; SIGINT is sent when it is cancelled. | |
ctx context.Context | |
Cmd *exec.Cmd | |
} | |
func commandContext(ctx context.Context, name string, arg ...string) cmd { | |
c := exec.Command(name, arg...) | |
// Force subprocesses into their own process group, rather than being in the | |
// same process group as the dep process. Because Ctrl-C sent from a | |
// terminal will send the signal to the entire currently running process | |
// group, this allows us to directly manage the issuance of signals to | |
// subprocesses. | |
c.SysProcAttr = &syscall.SysProcAttr{ | |
Setpgid: true, | |
Pgid: 0, | |
} | |
return cmd{ctx: ctx, Cmd: c} | |
} | |
// CombinedOutput is like (*os/exec.Cmd).CombinedOutput except that it | |
// terminates subprocesses gently (via os.Interrupt), but resorts to Kill if | |
// the subprocess fails to exit after 1 minute. | |
func (c cmd) CombinedOutput() ([]byte, error) { | |
// Adapted from (*os/exec.Cmd).CombinedOutput | |
if c.Cmd.Stdout != nil { | |
return nil, errors.New("exec: Stdout already set") | |
} | |
if c.Cmd.Stderr != nil { | |
return nil, errors.New("exec: Stderr already set") | |
} | |
var b bytes.Buffer | |
c.Cmd.Stdout = &b | |
c.Cmd.Stderr = &b | |
if err := c.Cmd.Start(); err != nil { | |
return nil, err | |
} | |
log.Printf("started process %v", c.Cmd.Process.Pid) | |
// Adapted from (*os/exec.Cmd).Start | |
waitDone := make(chan struct{}) | |
defer close(waitDone) | |
go func() { | |
select { | |
case <-c.ctx.Done(): | |
log.Printf("interrupt %q", c.Cmd.Args) | |
if err := c.Cmd.Process.Signal(os.Interrupt); err != nil { | |
// If an error comes back from attempting to signal, proceed | |
// immediately to hard kill. | |
log.Printf("immediate hard kill %q", c.Cmd.Args) | |
_ = c.Cmd.Process.Kill() | |
} else { | |
defer time.AfterFunc(time.Minute, func() { | |
log.Printf("delayed hard kill %q", c.Cmd.Args) | |
_ = c.Cmd.Process.Kill() | |
}).Stop() | |
<-waitDone | |
} | |
case <-waitDone: | |
} | |
}() | |
err := c.Cmd.Wait() | |
return b.Bytes(), err | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment