You have to do exactly what you are doing currently, except that you have to do it with loops. Here is a full example which do nothing more than your own example case:
package main
import (
"os"
"os/exec"
)
func main() {
var err error // To store error during the processing. You should always check for errors and handle them. That's not an option.
var commands []*exec.Cmd // The slice that will store the commands.
// You own commands
commands = append(commands, exec.Command("ls"))
commands = append(commands, exec.Command("wc", "-l"))
// Connect each command input to the output of the previous command (starting with the second command, obviously)
for i := 1; i < len(commands); i++ {
commands[i].Stdin, err = commands[i - 1].StdoutPipe()
if err != nil {
panic(err)
}
}
commands[len(commands)-1].Stdout = os.Stdout // Last command output is connected to the standard output
// Start each command. note that the reverse order isn't mandatory, you just have to avoid runing the first command now
for i := len(commands) - 1; i > 0; i-- {
err = commands[i].Start()
if err != nil {
panic(err)
}
}
// Run the first command
commands[0].Run()
// Then wait for each subsequent command to finish
for i := 1; i < len(commands); i++ {
err = commands[i].Wait()
if err != nil {
panic(err)
}
}
}