Skip to content

Instantly share code, notes, and snippets.

@matejb
Created August 10, 2017 08:27
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save matejb/c88bd07c2c36e4ebed31644fbcceac15 to your computer and use it in GitHub Desktop.
Save matejb/c88bd07c2c36e4ebed31644fbcceac15 to your computer and use it in GitHub Desktop.
simple Go pipe executor
package main
import (
"bytes"
"fmt"
"log"
"os/exec"
"strings"
)
func main() {
out, err := pipe("ls -l | grep io | wc -l")
if err != nil {
log.Println(err)
}
fmt.Println(out)
}
func pipe(line string) (output string, err error) {
cmds := strings.Split(line, "|")
lastIx := len(cmds) - 1
var outputBuf bytes.Buffer
type chainLink struct {
info string
wait *exec.Cmd
execute *exec.Cmd
}
chain := make([]chainLink, 0, len(cmds))
// make chain
for ix := range cmds {
cmd := strings.TrimSpace(cmds[ix])
cmdArgs := strings.Split(cmd, " ")
link := chainLink{
info: cmd,
execute: exec.Command(cmdArgs[0], cmdArgs[1:]...),
}
if ix > 0 {
// link out to in
prevOut, err := chain[ix-1].execute.StdoutPipe()
if err != nil {
return "", fmt.Errorf("failed getting previous stdout on %q: %s", chain[ix-1].info, err)
}
link.execute.Stdin = prevOut
// add wait for previous cmd
link.wait = chain[ix-1].execute
}
if ix == lastIx {
// last one writes to buffer
link.execute.Stdout = &outputBuf
}
chain = append(chain, link)
}
// execute chain
var link chainLink
for {
link, chain = chain[0], chain[1:]
if err := link.execute.Start(); err != nil {
return "", fmt.Errorf("failed starting on %q: %s", link.info, err)
}
if link.wait != nil {
if err := link.wait.Wait(); err != nil {
return "", fmt.Errorf("failed waiting for previous on %q: %s", link.info, err)
}
}
if len(chain) == 0 {
if err := link.execute.Wait(); err != nil {
return "", fmt.Errorf("failed waiting on %q: %s", link.info, err)
}
return outputBuf.String(), nil
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment