Skip to content
Snippets Groups Projects
Unverified Commit 9847d19e authored by Thomas Poignant's avatar Thomas Poignant Committed by GitHub
Browse files

Add Slice() sink (fixes #47)


* Add Slice() sink (fixes #47)

* Add test with empty pipe

* Apply suggestions from code review

Co-Authored-By: default avatarJohn Arundel <john@bitfieldconsulting.com>

* Not using type as variable name

* Using cmp to deep compare

* Not testing ListFiles in the test method for SliceSink

* Remove examples/visitors/go.sum who should not be commited

* An empty pipe should return an empty Slice

* Better example for Slice

* Ignore all go.sum

Co-authored-by: default avatarJohn Arundel <john@bitfieldconsulting.com>

* use cmp.diff when printing error

* Remove comma in doc

* Move Slice after SHA256

* Edit comments

* Adding trailing blank line:

* Single line with embeded new lines

* Code style

* Range over pids

* Code style

* Using EachLine to split on each lines

* Slice is supposed to be after SHA256

* Change order of params in cmp.Equal

* Better explanation in test

* typo

* Better example who don't use system specific command

* still an order problem

* Order problem

* Add comment to the example for slice

* Move the test to a t.Run format

* Change order for cmp.Equal

* Add example file

* Tweaks to SliceSink

Co-authored-by: default avatarJohn Arundel <john@bitfieldconsulting.com>
Co-authored-by: default avatarThomas Poignant <thomas.poignant@adevinta.com>
parent 34693545
Branches
Tags v0.18.0
No related merge requests found
......@@ -4,5 +4,6 @@ examples/cat2/cat2
examples/echo/echo
examples/head/head
examples/visitors/visitors
examples/*/go.sum
.vscode/settings.json
examples/ls/ls
......@@ -121,6 +121,7 @@ John is also a [Kubernetes and cloud infrastructure consultant](https://bitfield
- [Read](#read)
- [SHA256Sum](#sha256sum)
- [Why not MD5?](#why-not-md5)
- [Slice](#slice-1)
- [Stdout](#stdout)
- [String](#string)
- [WriteFile](#writefile)
......@@ -452,13 +453,13 @@ fmt.Println(files)
`Slice()` creates a pipe from a slice of strings, one per line.
```go
p := Slice([]string{"1", "2", "3"})
p := script.Slice([]string{"1", "2", "3"})
output, err := p.String()
fmt.Println(output)
// Output:
1
2
3
// 1
// 2
// 3
```
## Stdin
......@@ -805,6 +806,17 @@ sha256Sum, err := script.File("test.txt").SHA256Sum()
[MD5 is insecure](https://en.wikipedia.org/wiki/MD5#Security).
## Slice
`Slice()` returns the contents of the pipe as a slice of strings, one element per line, plus an error. An empty pipe will produce an empty slice. A pipe containing a single empty line (that is, a single `\n` character) will produce a slice of one element which is the empty string.
```go
args, err := script.Args().Slice()
for _, a := range args {
fmt.Println(a)
}
```
## Stdout
`Stdout()` writes the contents of the pipe to the program's standard output. It returns the number of bytes written, or an error:
......@@ -871,6 +883,7 @@ Since `script` is designed to help you write system administration programs, a f
* [least_freq](examples/least_freq/main.go)
* [ls](examples/ls/main.go)
* [sha256sum](examples/sha256sum/main.go)
* [slice](examples/slice/main.go)
* [tail](examples/tail/main.go)
* [visitors](examples/visitors/main.go)
......
Line containing the word 'error'
Another line containing an error message
Oh no, an error occurred!
Here's another error
More errors
To err is human, but forgiveness is surprisingly difficult.
something something error happened
error: a suffusion of yellow
An UNKNOWN ERROR occurred
Drive Z: does not exist, please try again.
error deactivating tyrannosaur fence: you didn't say the magic word!
error: the operation completed successfully
Fatal error: keyboard not present. Press F1 to continue.
\ No newline at end of file
package main
import (
"fmt"
"log"
"github.com/bitfield/script"
)
// This program reads all the files supplied to it on the command line, and
// prints out the first ten lines matching the string 'error', with an 'ERROR:'
// prefix.
//
// For example:
// go run main.go errors.txt
// ERROR: Line containing the word 'error'
// ...
func main() {
errors, err := script.Args().Concat().Match("error").First(10).Slice()
if err != nil {
log.Fatal(err)
}
for _, e := range errors {
fmt.Printf("ERROR: %s\n", e)
}
}
......@@ -107,10 +107,6 @@ func doMethodsOnPipe(t *testing.T, p *Pipe, kind string) {
p.Concat()
action = "CountLines()"
p.CountLines()
action = "SHA256Sums()"
p.SHA256Sums()
action = "SHA256Sum()"
p.SHA256Sum()
action = "Dirname()"
p.Dirname()
action = "EachLine()"
......@@ -141,6 +137,12 @@ func doMethodsOnPipe(t *testing.T, p *Pipe, kind string) {
p.ReplaceRegexp(regexp.MustCompile(".*"), "")
action = "SetError()"
p.SetError(nil)
action = "SHA256Sums()"
p.SHA256Sums()
action = "SHA256Sum()"
p.SHA256Sum()
action = "Slice()"
p.Slice()
action = "Stdout()"
// Ensure we don't clash with TestStdout
stdoutM.Lock()
......
......@@ -64,6 +64,19 @@ func (p *Pipe) SHA256Sum() (string, error) {
return encodedCheckSum, nil
}
// Slice returns the contents of the pipe as a slice of strings, one element per line, or an error.
// If there is an error reading the pipe, the pipe's error status is also set.
func (p *Pipe) Slice() ([]string, error) {
if p == nil || p.Error() != nil {
return nil, p.Error()
}
result := []string{}
p.EachLine(func(line string, out *strings.Builder) {
result = append(result, line)
})
return result, p.Error()
}
// Stdout writes the contents of the pipe to the program's standard output. It
// returns the number of bytes successfully written, plus a non-nil error if the
// write failed or if there was an error reading from the pipe. If the pipe has
......
......@@ -2,6 +2,7 @@ package script
import (
"bytes"
"github.com/google/go-cmp/cmp"
"io/ioutil"
"os"
"testing"
......@@ -31,6 +32,11 @@ func doSinksOnPipe(t *testing.T, p *Pipe, kind string) {
if err != nil {
t.Error(err)
}
action = "Slice()"
_, err = p.Slice()
if err != nil {
t.Error(err)
}
action = "WriteFile()"
_, err = p.WriteFile("testdata/tmp" + kind)
defer os.Remove("testdata/tmp" + kind)
......@@ -166,6 +172,61 @@ func TestSHA256Sum(t *testing.T) {
}
}
func TestSliceSink(t *testing.T) {
tests := []struct {
name string
fields *Pipe
want []string
wantErr bool
}{
{
name: "Multiple lines pipe",
fields: Echo("testdata/multiple_files/1.txt\ntestdata/multiple_files/2.txt\ntestdata/multiple_files/3.tar.zip\n"),
want: []string{
"testdata/multiple_files/1.txt",
"testdata/multiple_files/2.txt",
"testdata/multiple_files/3.tar.zip",
},
wantErr: false,
},
{
name: "Empty pipe",
fields: Echo(""),
want: []string{},
wantErr: false,
},
{
name: "Single newline",
fields: Echo("\n"),
want: []string{""},
wantErr: false,
},
{
name: "Empty line between two existing lines",
fields: Echo("testdata/multiple_files/1.txt\n\ntestdata/multiple_files/3.tar.zip"),
want: []string{
"testdata/multiple_files/1.txt",
"",
"testdata/multiple_files/3.tar.zip",
},
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
p := tt.fields
got, err := p.Slice()
if (err != nil) != tt.wantErr {
t.Errorf("Slice() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !cmp.Equal(tt.want, got) {
t.Error(cmp.Diff(tt.want, got))
}
})
}
}
func TestStdout(t *testing.T) {
t.Parallel()
// Temporarily point os.Stdout to a file so that we can capture it for
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment