Skip to content

Instantly share code, notes, and snippets.

@tt
Created April 30, 2015 18:47
Show Gist options
  • Save tt/98f3a80d1b4ebaa3882f to your computer and use it in GitHub Desktop.
Save tt/98f3a80d1b4ebaa3882f to your computer and use it in GitHub Desktop.
busl tee utility
package main
import (
"io"
"io/ioutil"
"log"
"os"
"os/exec"
"sync"
)
func main() {
cmd := exec.Command(os.Args[1], os.Args[2:len(os.Args)]...)
busl := ioutil.Discard
stdout := io.MultiWriter(busl, os.Stdout)
stderr := io.MultiWriter(busl, os.Stderr)
if errs, err := attachCmd(cmd, os.Stdin, stdout, stderr); err != nil {
log.Fatal(err)
} else if err := cmd.Start(); err != nil {
log.Fatal(err)
} else if err := <-errs; err != nil {
log.Fatal(err)
} else if err := cmd.Wait(); err != nil {
log.Fatal(err)
}
}
func attachCmd(cmd *exec.Cmd, stdin io.Reader, stdout, stderr io.Writer) (<-chan error, error) {
var wg sync.WaitGroup
wg.Add(2)
errs := make(chan error, 1)
if stdin != nil {
if stdinPipe, err := cmd.StdinPipe(); err != nil {
return nil, err
} else {
go func() {
defer stdinPipe.Close()
io.Copy(stdinPipe, stdin)
}()
}
}
copyFunc := func(w io.Writer, r io.Reader) {
defer wg.Done()
if _, err := io.Copy(w, r); err != nil {
if cmd.Process != nil {
cmd.Process.Kill()
cmd.Process.Wait()
}
errs <- err
}
}
if stdoutPipe, err := cmd.StdoutPipe(); err != nil {
return nil, err
} else {
go copyFunc(stdout, stdoutPipe)
}
if stderrPipe, err := cmd.StderrPipe(); err != nil {
return nil, err
} else {
go copyFunc(stderr, stderrPipe)
}
go func() {
defer close(errs)
wg.Wait()
}()
return errs, nil
}
@cyx
Copy link

cyx commented May 1, 2015

Worth pointing out that maybe this whole pattern should be encapsulated in a library (and then shared between busltee / endosome).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment