Golang processes: Difference between revisions
From wikinotes
(13 intermediate revisions by the same user not shown) | |||
Line 1: | Line 1: | ||
Start/Manage a process, or get information about system processes. | |||
= Documentation = | = Documentation = | ||
Line 56: | Line 51: | ||
high-level/friendly | high-level/friendly | ||
<syntaxhighlight lang="go"> | <syntaxhighlight lang="go"> | ||
// | // 1. create command, | ||
// | // 2. modify it's exec.Cmd struct (ex. stdin,stdout) | ||
// | // 3. run process | ||
// 4. cmd.ProcessState has info about process, including exitcode | |||
import "os" | import "os" | ||
import "os/exec" | import "os/exec" | ||
Line 65: | Line 61: | ||
stdout, err := cmd.Output() | stdout, err := cmd.Output() | ||
// must be read-from/written-to in go routine! (can be anonymous) | |||
// ex. go func() { defer stdin.Close(); stdin.Write([]byte("foo")); }() | |||
reader, err := cmd.StderrPipe() | reader, err := cmd.StderrPipe() | ||
reader, err := cmd.StdoutPipe() | reader, err := cmd.StdoutPipe() | ||
writer, err := cmd.StdinPipe() | writer, err := cmd.StdinPipe() | ||
cmd.Process.Pid // get pid (after starting process) | |||
err := cmd.Run() // run and wait | err := cmd.Run() // run and wait | ||
err := cmd.Start() // run, don't wait | err := cmd.Start() // run, don't wait | ||
err := cmd.Wait() // wait for proces to finish | err := cmd.Wait() // wait for proces to finish | ||
</syntaxhighlight> | </syntaxhighlight> | ||
{{ expand | |||
| StdinPipe example | |||
| | |||
<syntaxhighlight lang="go"> | |||
cmd := exec.Command("pandoc", "-f", "mediawiki", "-t", "rst") | |||
stdin, _ := cmd.StdinPipe() | |||
ch := make(chan, error) | |||
go func(ch chan<- error) { | |||
defer stdin.Close() | |||
_, err := stdin.Write([]byte("abc")) | |||
ch <- err | |||
}(ch) | |||
cmd.Run() | |||
</syntaxhighlight> | |||
}} | |||
{{ expand | |||
| Stdout/Stderr example | |||
| | |||
<syntaxhighlight lang="go"> | |||
cmd := exec.Command("pandoc", "-f", "mediawiki", "-t", "rst", "foo.mediawiki") | |||
stdout, _ := cmd.StdoutPipe() | |||
defer stdout.Close() | |||
cmd.Start() | |||
out, _ := io.ReadAll(stdout) | |||
cmd.Wait() | |||
</syntaxhighlight> | |||
}} | |||
{{ expand | |||
| Stdin/Stdout/Stderr example | |||
| | |||
Pipes returned from <code>exec.Cmd</code> methods are closed automatically when <code>exec.Cmd.Wait()</code> is called.<br> | |||
When manipulating both Stdin/Stdout, the process may wait for Stdin to be closed to complete.<br> | |||
You'll need to close it manually, and surface Close() errors using channels. | |||
<syntaxhighlight lang="go"> | |||
func (this *Cmd) Execute(stdin io.Reader) (render string, errs []error) { | |||
// record goroutine errors | |||
wg := sync.WaitGroup{} | |||
wg.Add(1) | |||
ch := make(chan error, 2) | |||
defer func(ch <-chan error) { | |||
wg.Wait() | |||
err := <-ch | |||
if err != nil { | |||
errs = append(errs, err) | |||
} | |||
}(ch) | |||
// build pipes | |||
stdout, err := this.StdoutPipe() | |||
if err != nil { | |||
errs = append(errs, err) | |||
} | |||
stderr, err := this.StderrPipe() | |||
if err != nil { | |||
errs = append(errs, err) | |||
} | |||
stdinW, err := this.StdinPipe() | |||
if err != nil { | |||
errs = append(errs, err) | |||
} | |||
if len(errs) > 0 { | |||
return "", errs | |||
} | |||
// write stdin | |||
go func(ch chan<- error) { | |||
defer func() { | |||
err := stdinW.Close() | |||
if err != nil { | |||
ch <- err | |||
} | |||
close(ch) | |||
wg.Done() | |||
}() | |||
data, err := io.ReadAll(stdin) | |||
if err != nil { | |||
ch <- err | |||
return | |||
} | |||
if _, err = stdinW.Write(data); err != nil { | |||
ch <- err | |||
} | |||
}(ch) | |||
// run command | |||
err = this.Start() | |||
if err != nil { | |||
errs = append(errs, err) | |||
return "", errs | |||
} | |||
outAll, err := io.ReadAll(stdout) | |||
if err != nil { | |||
errs = append(errs, err) | |||
} | |||
errAll, err := io.ReadAll(stderr) | |||
if err != nil { | |||
errs = append(errs, err) | |||
} | |||
if len(errs) > 0 { | |||
return "", errs | |||
} | |||
err = this.Wait() | |||
if err != nil { | |||
logger.Debugf("STDERR:\n%s", errAll) | |||
errs = append(errs, err) | |||
} | |||
return string(outAll), errs | |||
} | |||
</syntaxhighlight> | |||
}} | |||
low-level | low-level | ||
Line 93: | Line 212: | ||
</blockquote><!-- Subprocess --> | </blockquote><!-- Subprocess --> | ||
= | = Reading from STDIN = | ||
<blockquote> | <blockquote> | ||
<syntaxhighlight lang="go"> | |||
stat, _ := os.Stdin.Stat() | |||
</blockquote><!-- | if (stat.Mode() & os.ModeNamedPipe) == 0 { | ||
fmt.Println("Nothing to read on stdin") | |||
os.Exit(1) | |||
} else { | |||
var reader = bufio.NewReader(os.Stdin) | |||
for { | |||
line, err := reader.ReadString('\n') | |||
if err == io.EOF { | |||
break | |||
} | |||
fmt.Print(line) | |||
time.Sleep(1 * time.Second) | |||
} | |||
} | |||
</syntaxhighlight> | |||
</blockquote><!-- Reading from STDIN --> |
Latest revision as of 21:59, 17 July 2022
Start/Manage a process, or get information about system processes.
Documentation
os
https://pkg.go.dev/os@go1.18.3
Current Process
import "os" import "os/user" os.Executable() os.Getpid() os.Getppid() os.Getuid() os.Getgid() os.Exit(1) // exit process with exitcode '1' // environment envvars := os.Environ() // ["FOO=bar", "BAR=baz", ...] pwd, err := os.Getwd() // get current pwd/cwd user.Current() // 'will'
Manage Processes
import "syscall" import "os" import "os/signal" proc := os.Process.FindProcess(1234) // find process with pid proc.Kill() // kill process (-9/SIGKILL) proc.Signal(os.Iterrupt) // send SIGINT to process (looks like you can use any syscall.SIG*) proc.Wait() // wait for process to end syscall.Kill(1234, syscall.SIGINT) // send SIGINT to pid 1234
Subprocess
high-level/friendly
// 1. create command, // 2. modify it's exec.Cmd struct (ex. stdin,stdout) // 3. run process // 4. cmd.ProcessState has info about process, including exitcode import "os" import "os/exec" cmd := exec.Command("netstat", "-an") stdout, err := cmd.Output() // must be read-from/written-to in go routine! (can be anonymous) // ex. go func() { defer stdin.Close(); stdin.Write([]byte("foo")); }() reader, err := cmd.StderrPipe() reader, err := cmd.StdoutPipe() writer, err := cmd.StdinPipe() cmd.Process.Pid // get pid (after starting process) err := cmd.Run() // run and wait err := cmd.Start() // run, don't wait err := cmd.Wait() // wait for proces to finishStdinPipe example
cmd := exec.Command("pandoc", "-f", "mediawiki", "-t", "rst") stdin, _ := cmd.StdinPipe() ch := make(chan, error) go func(ch chan<- error) { defer stdin.Close() _, err := stdin.Write([]byte("abc")) ch <- err }(ch) cmd.Run()
Stdout/Stderr example
cmd := exec.Command("pandoc", "-f", "mediawiki", "-t", "rst", "foo.mediawiki") stdout, _ := cmd.StdoutPipe() defer stdout.Close() cmd.Start() out, _ := io.ReadAll(stdout) cmd.Wait()Stdin/Stdout/Stderr example
Pipes returned fromexec.Cmd
methods are closed automatically whenexec.Cmd.Wait()
is called.
When manipulating both Stdin/Stdout, the process may wait for Stdin to be closed to complete.
You'll need to close it manually, and surface Close() errors using channels.func (this *Cmd) Execute(stdin io.Reader) (render string, errs []error) { // record goroutine errors wg := sync.WaitGroup{} wg.Add(1) ch := make(chan error, 2) defer func(ch <-chan error) { wg.Wait() err := <-ch if err != nil { errs = append(errs, err) } }(ch) // build pipes stdout, err := this.StdoutPipe() if err != nil { errs = append(errs, err) } stderr, err := this.StderrPipe() if err != nil { errs = append(errs, err) } stdinW, err := this.StdinPipe() if err != nil { errs = append(errs, err) } if len(errs) > 0 { return "", errs } // write stdin go func(ch chan<- error) { defer func() { err := stdinW.Close() if err != nil { ch <- err } close(ch) wg.Done() }() data, err := io.ReadAll(stdin) if err != nil { ch <- err return } if _, err = stdinW.Write(data); err != nil { ch <- err } }(ch) // run command err = this.Start() if err != nil { errs = append(errs, err) return "", errs } outAll, err := io.ReadAll(stdout) if err != nil { errs = append(errs, err) } errAll, err := io.ReadAll(stderr) if err != nil { errs = append(errs, err) } if len(errs) > 0 { return "", errs } err = this.Wait() if err != nil { logger.Debugf("STDERR:\n%s", errAll) errs = append(errs, err) } return string(outAll), errs }
low-level
// 1. create ProcAttr // 2. start process // 3. wait for process // 4. get info from it's ProcessState import "os" import "os/exec" psAttr := os.ProcAttr{ Dir: "/var/tmp", Env: []string{"FOO=BAR"}, } ps := os.StartProcess("netstat", []string{"-a", "-n"}, psAttr) pstate, err := ps.Wait() pstate.ExitCode()
Reading from STDIN
stat, _ := os.Stdin.Stat() if (stat.Mode() & os.ModeNamedPipe) == 0 { fmt.Println("Nothing to read on stdin") os.Exit(1) } else { var reader = bufio.NewReader(os.Stdin) for { line, err := reader.ReadString('\n') if err == io.EOF { break } fmt.Print(line) time.Sleep(1 * time.Second) } }