diff --git a/README.md b/README.md index 4a9258d..8db96d0 100644 --- a/README.md +++ b/README.md @@ -172,7 +172,9 @@ In the `ping` example, we knew the exact arguments we wanted to send the command We might like to be able to run the external command repeatedly, each time passing it the next line of data from the pipe as an argument. No worries: ```go -script.Args().ExecForEach("ping -c 1 {{.}}").Stdout() +// {{.Raw}} is original input +// {{index .Cols N}} N indicates which column; +script.Args().ExecForEach("ping -c 1 {{.Raw}}").Stdout() ``` That `{{.}}` is standard Go template syntax; it'll substitute each line of data from the pipe into the command line before it's executed. You can write as fancy a Go template expression as you want here (but this simple example probably covers most use cases). diff --git a/script.go b/script.go index 8041794..a4e2920 100644 --- a/script.go +++ b/script.go @@ -399,6 +399,12 @@ func (p *Pipe) Exec(cmdLine string) *Pipe { }) } +// inputFields for Go template +type inputFields struct { + Raw string // original input + Cols []string // columns of line +} + // ExecForEach renders cmdLine as a Go template for each line of input, running // the resulting command, and produces the combined output of all these // commands in sequence. See [Pipe.Exec] for error handling details. @@ -416,7 +422,12 @@ func (p *Pipe) ExecForEach(cmdLine string) *Pipe { scanner := bufio.NewScanner(r) for scanner.Scan() { cmdLine := strings.Builder{} - err := tpl.Execute(&cmdLine, scanner.Text()) + line := scanner.Text() + fields := strings.Fields(line) + err := tpl.Execute(&cmdLine, inputFields{ + Raw: line, + Cols: fields, + }) if err != nil { return err } diff --git a/script_unix_test.go b/script_unix_test.go index 50fc47f..7365647 100644 --- a/script_unix_test.go +++ b/script_unix_test.go @@ -52,7 +52,7 @@ func TestExecErrorsRunningShellCommandWithUnterminatedStringArgument(t *testing. func TestExecForEach_RunsEchoWithABCAndGetsOutputABC(t *testing.T) { t.Parallel() - p := script.Echo("a\nb\nc\n").ExecForEach("echo {{.}}") + p := script.Echo("a\nb\nc\n").ExecForEach("echo {{.Raw}}") if p.Error() != nil { t.Fatal(p.Error()) } @@ -82,6 +82,22 @@ func TestExecForEach_CorrectlyEvaluatesTemplateContainingIfStatement(t *testing. } } +func TestExecForEach_GetColunmsByIndex(t *testing.T) { + t.Parallel() + p := script.Echo("a b c d e\naa bb cc dd ee").ExecForEach("echo {{index .Cols 1}} {{index .Cols 4}}; ") + if p.Error() != nil { + t.Fatal(p.Error()) + } + want := "b e;\nbb ee;\n" + got, err := p.String() + if err != nil { + t.Fatal(err) + } + if want != got { + t.Error(cmp.Diff(want, got)) + } +} + func TestExecPipesDataToExternalCommandAndGetsExpectedOutput(t *testing.T) { t.Parallel() p := script.File("testdata/hello.txt").Exec("cat") @@ -182,9 +198,13 @@ func ExamplePipe_Exec() { } func ExamplePipe_ExecForEach() { - script.Echo("a\nb\nc\n").ExecForEach("echo {{.}}").Stdout() + script.Echo("a\nb\nc\n").ExecForEach("echo {{.Raw}}").Stdout() + script.Echo("a aa\nb bb\nc cc\n").ExecForEach("echo {{index .Cols 1}} {{index .Cols 0}}").Stdout() // Output: // a // b // c + // aa a + // bb b + // cc c }