github.com/blend/go-sdk@v1.20220411.3/sh/pipe.go (about) 1 /* 2 3 Copyright (c) 2022 - Present. Blend Labs, Inc. All rights reserved 4 Use of this source code is governed by a MIT license that can be found in the LICENSE file. 5 6 */ 7 8 package sh 9 10 import ( 11 "io" 12 "os" 13 "os/exec" 14 "sync" 15 ) 16 17 // Pipe runs commands in sequence, piping combined output to the standard in of the next command. 18 func Pipe(commands ...*exec.Cmd) error { 19 wg := sync.WaitGroup{} 20 wg.Add(len(commands)) 21 22 errors := make(chan error, len(commands)) 23 readers := make([]io.Reader, len(commands)) 24 writers := make([]io.Writer, len(commands)) 25 for index := 0; index < len(commands); index++ { 26 // set up pipes 27 readers[index], writers[index] = io.Pipe() 28 29 // wire up pipes 30 switch { 31 case index == 0: // the first command 32 commands[index].Stdin = os.Stdin 33 commands[index].Stdout = writers[index] 34 commands[index].Stderr = writers[index] 35 case index == len(commands)-1: // the last command 36 commands[index].Stdin = readers[index-1] 37 commands[index].Stdout = os.Stdout 38 commands[index].Stderr = os.Stderr 39 default: // intermediate commands 40 commands[index].Stdin = readers[index-1] 41 commands[index].Stdout = writers[index] 42 commands[index].Stderr = writers[index] 43 } 44 go func(index int, cmd *exec.Cmd) { 45 defer wg.Done() 46 if index > 0 { 47 defer func() { 48 if typed, ok := cmd.Stdout.(*io.PipeWriter); ok { 49 typed.Close() 50 } 51 if typed, ok := cmd.Stderr.(*io.PipeWriter); ok { 52 typed.Close() 53 } 54 if typed, ok := cmd.Stdin.(*io.PipeReader); ok { 55 typed.Close() 56 } 57 }() 58 } 59 if err := cmd.Run(); err != nil { 60 if !IsEPIPE(err) { 61 errors <- err 62 } 63 } 64 }(index, commands[index]) 65 } 66 wg.Wait() 67 if len(errors) > 0 { 68 return <-errors 69 } 70 return nil 71 }