script

package module
v0.18.5 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 5, 2022 License: MIT Imports: 18 Imported by: 202

README

Go ReferenceGo Report CardMentioned in Awesome GoCircleCI

import "github.com/bitfield/script"

Magical gopher logo

What is script?

script is a Go library for doing the kind of tasks that shell scripts are good at: reading files, executing subprocesses, counting lines, matching strings, and so on.

Why shouldn't it be as easy to write system administration programs in Go as it is in a typical shell? script aims to make it just that easy.

Shell scripts often compose a sequence of operations on a stream of data (a pipeline). This is how script works, too.

Read more: Scripting with Go

Quick start: Unix equivalents

If you're already familiar with shell scripting and the Unix toolset, here is a rough guide to the equivalent script operation for each listed Unix command.

Unix / shell script equivalent
(any program name) Exec()
[ -f FILE ] IfExists()
> WriteFile()
>> AppendFile()
$* Args()
basename Basename()
cat File() / Concat()
cut Column()
dirname Dirname()
echo Echo()
grep Match() / MatchRegexp()
grep -v Reject() / RejectRegexp()
head First()
find -type f FindFiles
ls ListFiles()
sed Replace() / ReplaceRegexp()
sha256sum SHA256Sum() / SHA256Sums()
tail Last()
uniq -c Freq()
wc -l CountLines()
xargs ExecForEach()

Some examples

Let's see some simple examples. Suppose you want to read the contents of a file as a string:

contents, err := script.File("test.txt").String()

That looks straightforward enough, but suppose you now want to count the lines in that file.

numLines, err := script.File("test.txt").CountLines()

For something a bit more challenging, let's try counting the number of lines in the file that match the string "Error":

numErrors, err := script.File("test.txt").Match("Error").CountLines()

But what if, instead of reading a specific file, we want to simply pipe input into this program, and have it output only matching lines (like grep)?

script.Stdin().Match("Error").Stdout()

That was almost too easy! So let's pass in a list of files on the command line, and have our program read them all in sequence and output the matching lines:

script.Args().Concat().Match("Error").Stdout()

Maybe we're only interested in the first 10 matches. No problem:

script.Args().Concat().Match("Error").First(10).Stdout()

What's that? You want to append that output to a file instead of printing it to the terminal? You've got some attitude, mister.

script.Args().Concat().Match("Error").First(10).AppendFile("/var/log/errors.txt")

Real use cases

Let's use script to write a program that system administrators might actually need. One thing I often find myself doing is counting the most frequent visitors to a website over a given period of time. Given an Apache log in the Common Log Format like this:

212.205.21.11 - - [30/Jun/2019:17:06:15 +0000] "GET / HTTP/1.1" 200 2028 "https://example.com/ "Mozilla/5.0 (Linux; Android 8.0.0; FIG-LX1 Build/HUAWEIFIG-LX1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.156 Mobile Safari/537.36"

we would like to extract the visitor's IP address (the first column in the logfile), and count the number of times this IP address occurs in the file. Finally, we might like to list the top 10 visitors by frequency. In a shell script we might do something like:

cut -d' ' -f 1 access.log |sort |uniq -c |sort -rn |head

There's a lot going on there, and it's pleasing to find that the equivalent script program is quite brief:

package main

import (
	"github.com/bitfield/script"
)

func main() {
	script.Stdin().Column(1).Freq().First(10).Stdout()
}

Let's try it out with some sample data:

cd examples/visitors
go run main.go <access.log

16 176.182.2.191
 7 212.205.21.11
 1 190.253.121.1
 1 90.53.111.17

Documentation

See pkg.go.dev for the full documentation, or read on for a summary.

Sources

These are functions that create a pipe with a given contents:

Source Contents
Args command-line arguments
Echo a string
Exec command output
File file contents
FindFiles recursive file listing
IfExists do something only if some file exists
ListFiles file listing (including wildcards)
Slice slice elements, one per line
Stdin standard input

Filters

Filters are methods on an existing pipe that also return a pipe, allowing you to chain filters indefinitely. The filters modify each line of their input according to the following rules:

Filter Results
Basename removes leading path components from each line, leaving only the filename
Column Nth column of input
Concat contents of multiple files
Dirname removes filename from each line, leaving only leading path components
EachLine user-supplied function
Echo all input replaced by given string
Exec filtered through external command
ExecForEach execute given command template for each line of input
First first N lines
Freq frequency count of unique input lines, most frequent first
Join replace all newlines with spaces
Last last N lines
Match matching lines
MatchRegexp matching lines
Reject non-matching lines
RejectRegexp non-matching lines
Replace matching lines replaced with string
ReplaceRegexp matching lines replaced with string
SHA256Sums SHA-256 hashes of each listed file

Sinks

Sinks are methods that return some data from a pipe, ending the pipeline and extracting its full contents in a specified way:

Sink Destination Results
AppendFile appended to file, creating if it exists bytes written, error
Bytes data as []byte, error
CountLines number of lines, error
Read given []byte bytes read, error
SHA256Sum SHA-256 hash, error
Slice data as []string, error
Stdout standard output bytes written, error
String data as string, error
WriteFile specified file, truncating if it exists bytes written, error

Examples

Since script is designed to help you write system administration programs, a few simple examples of such programs are included in the examples directory.

Contributing

See the contributor's guide for some helpful tips if you'd like to contribute to the script project.

Gopher image by MariaLetta

Documentation

Overview

Package script aims to make it easy to write shell-type scripts in Go, for general system administration purposes: reading files, counting lines, matching strings, and so on.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Pipe

type Pipe struct {
	Reader ReadAutoCloser
	// contains filtered or unexported fields
}

Pipe represents a pipe object with an associated ReadAutoCloser.

func Args added in v0.7.0

func Args() *Pipe

Args creates a pipe containing the program's command-line arguments, one per line.

Example
package main

import (
	"github.com/bitfield/script"
)

func main() {
	script.Args().Stdout()
	// prints command-line arguments
}

func Echo added in v0.3.0

func Echo(s string) *Pipe

Echo returns a pipe containing the supplied string.

Example
package main

import (
	"github.com/bitfield/script"
)

func main() {
	script.Echo("Hello, world!").Stdout()
}
Output:

Hello, world!

func Exec added in v0.5.0

func Exec(s string) *Pipe

Exec runs an external command and returns a pipe containing its combined output (stdout and stderr).

If the command had a non-zero exit status, the pipe's error status will also be set to the string "exit status X", where X is the integer exit status.

For convenience, you can get this value directly as an integer by calling ExitStatus on the pipe.

Even in the event of a non-zero exit status, the command's output will still be available in the pipe. This is often helpful for debugging. However, because String is a no-op if the pipe's error status is set, if you want output you will need to reset the error status before calling String.

Note that Exec can also be used as a filter, in which case the given command will read from the pipe as its standard input.

Example (Exitstatus)
package main

import (
	"fmt"

	"github.com/bitfield/script"
)

func main() {
	p := script.Exec("echo")
	fmt.Println(p.ExitStatus())
}
Output:

0
Example (Ok)
package main

import (
	"github.com/bitfield/script"
)

func main() {
	script.Exec("echo Hello, world!").Stdout()
}
Output:

Hello, world!

func File

func File(name string) *Pipe

File returns a *Pipe that reads from the specified file. This is useful for starting pipelines. If there is an error opening the file, the pipe's error status will be set.

Example
package main

import (
	"github.com/bitfield/script"
)

func main() {
	script.File("testdata/hello.txt").Stdout()
}
Output:

hello world

func FindFiles added in v0.16.0

func FindFiles(path string) *Pipe

FindFiles takes a directory path and returns a pipe listing all the files in the directory and its subdirectories recursively, one per line, like Unix `find -type f`. If the path doesn't exist or can't be read, the pipe's error status will be set.

Each line of the output consists of a slash-separated pathname, starting with the initial directory. For example, if the starting directory is "test", and it contains 1.txt and 2.txt:

test/1.txt test/2.txt

Example
package main

import (
	"github.com/bitfield/script"
)

func main() {
	script.FindFiles("testdata/multiple_files_with_subdirectory").Stdout()
}
Output:

testdata/multiple_files_with_subdirectory/1.txt
testdata/multiple_files_with_subdirectory/2.txt
testdata/multiple_files_with_subdirectory/3.tar.zip
testdata/multiple_files_with_subdirectory/dir/.hidden
testdata/multiple_files_with_subdirectory/dir/1.txt
testdata/multiple_files_with_subdirectory/dir/2.txt

func IfExists added in v0.14.0

func IfExists(filename string) *Pipe

IfExists tests whether the specified file exists, and returns a pipe whose error status reflects the result. If the file doesn't exist, the pipe's error status will be set, and if the file does exist, the pipe will have no error status. This can be used to do some operation only if a given file exists:

IfExists("/foo/bar").Exec("/usr/bin/something")

Example (False)
package main

import (
	"github.com/bitfield/script"
)

func main() {
	script.IfExists("doesntexist").Echo("found it").Stdout()
}
Example (True)
package main

import (
	"github.com/bitfield/script"
)

func main() {
	script.IfExists("./testdata/hello.txt").Echo("found it").Stdout()
}
Output:

found it

func ListFiles added in v0.11.0

func ListFiles(path string) *Pipe

ListFiles creates a pipe containing the files and directories matching the supplied path, one per line. The path can be the name of a directory (`/path/to/dir`), the name of a file (`/path/to/file`), or a glob (wildcard expression) conforming to the syntax accepted by filepath.Match (for example `/path/to/*`).

ListFiles does not recurse into subdirectories (use FindFiles for this).

Example
package main

import (
	"github.com/bitfield/script"
)

func main() {
	script.ListFiles("testdata/multiple_files_with_subdirectory").Stdout()
}
Output:

testdata/multiple_files_with_subdirectory/1.txt
testdata/multiple_files_with_subdirectory/2.txt
testdata/multiple_files_with_subdirectory/3.tar.zip
testdata/multiple_files_with_subdirectory/dir

func NewPipe

func NewPipe() *Pipe

NewPipe returns a pointer to a new empty pipe.

func Slice added in v0.11.0

func Slice(s []string) *Pipe

Slice returns a pipe containing each element of the supplied slice of strings, one per line.

Example
package main

import (
	"github.com/bitfield/script"
)

func main() {
	input := []string{"1", "2", "3"}
	script.Slice(input).Stdout()
}
Output:

1
2
3

func Stdin added in v0.6.0

func Stdin() *Pipe

Stdin returns a pipe that reads from the program's standard input.

func (*Pipe) AppendFile added in v0.4.0

func (p *Pipe) AppendFile(fileName string) (int64, error)

AppendFile appends the contents of the Pipe to the specified file, and closes the pipe after reading. If the file does not exist, it is created.

AppendFile returns the number of bytes successfully written, or an error. If there is an error reading or writing, the pipe's error status is also set.

func (*Pipe) Basename added in v0.14.0

func (p *Pipe) Basename() *Pipe

Basename reads a list of filepaths from the pipe, one per line, and removes any leading directory components from each line. So, for example, `/usr/local/bin/foo` would become just `foo`. This is the complementary operation to Dirname.

If a line is empty, Basename will produce '.'. Trailing slashes are removed. The behaviour of Basename is the same as filepath.Base (not by coincidence).

Example
package main

import (
	"github.com/bitfield/script"
)

func main() {
	input := []string{
		"",
		"/",
		"/root",
		"/tmp/example.php",
		"/var/tmp/",
		"./src/filters",
		"C:/Program Files",
	}
	script.Slice(input).Basename().Stdout()
}
Output:

.
/
root
example.php
tmp
filters
Program Files

func (*Pipe) Bytes added in v0.8.0

func (p *Pipe) Bytes() ([]byte, error)

Bytes returns the contents of the Pipe as a slice of byte, or an error. If there is an error reading, the pipe's error status is also set.

Example
package main

import (
	"fmt"

	"github.com/bitfield/script"
)

func main() {
	data, err := script.Echo("hello").Bytes()
	if err != nil {
		panic(err)
	}
	fmt.Println(data)
}
Output:

[104 101 108 108 111]

func (*Pipe) Close

func (p *Pipe) Close() error

Close closes the pipe's associated reader. This is always safe to do, because pipes created from a non-closable source will have an `ioutil.NopCloser` to call.

func (*Pipe) Column added in v0.9.0

func (p *Pipe) Column(col int) *Pipe

Column reads from the pipe, and returns a new pipe containing only the Nth column of each line in the input, where '1' means the first column, and columns are delimited by whitespace. Specifically, whatever Unicode defines as whitespace ('WSpace=yes'). If there is an error reading the pipe, the pipe's error status is also set.

Lines containing less than N columns will be ignored.

Example
package main

import (
	"github.com/bitfield/script"
)

func main() {
	input := []string{
		"PID   TT  STAT      TIME COMMAND",
		"  1   ??  Ss   873:17.62 /sbin/launchd",
		" 50   ??  Ss    13:18.13 /usr/libexec/UserEventAgent (System)",
		" 51   ??  Ss    22:56.75 /usr/sbin/syslogd",
	}
	script.Slice(input).Column(1).Stdout()
}
Output:

PID
1
50
51

func (*Pipe) Concat added in v0.8.0

func (p *Pipe) Concat() *Pipe

Concat reads a list of filenames from the pipe, one per line, and returns a pipe that reads all those files in sequence. If there are any errors (for example, non-existent files), these will be ignored, execution will continue, and the pipe's error status will not be set.

This makes it convenient to write programs that take a list of input files on the command line. For example:

script.Args().Concat().Stdout()

The list of files could also come from a file:

script.File("filelist.txt").Concat()

...or from the output of a command:

script.Exec("ls /var/app/config/").Concat().Stdout()

Each input file will be closed once it has been fully read. If any of the files can't be opened or read, `Concat` will simply skip these and carry on, without setting the pipe's error status. This mimics the behaviour of Unix `cat`.

Example
package main

import (
	"github.com/bitfield/script"
)

func main() {
	input := []string{
		"testdata/test.txt",
		"testdata/doesntexist.txt",
		"testdata/hello.txt",
	}
	script.Slice(input).Concat().Stdout()
}
Output:

This is the first line in the file.
Hello, world.
This is another line in the file.
hello world

func (*Pipe) CountLines

func (p *Pipe) CountLines() (int, error)

CountLines counts lines from the pipe's reader, and returns the integer result, or an error. If there is an error reading the pipe, the pipe's error status is also set.

Example
package main

import (
	"fmt"

	"github.com/bitfield/script"
)

func main() {
	n, err := script.Echo("a\nb\nc\n").CountLines()
	if err != nil {
		panic(err)
	}
	fmt.Println(n)
}
Output:

3

func (*Pipe) Dirname added in v0.14.0

func (p *Pipe) Dirname() *Pipe

Dirname reads a list of pathnames from the pipe, one per line, and returns a pipe that contains only the parent directories of each pathname. For example, `/usr/local/bin/foo` would become just `/usr/local/bin`. This is the complementary operation to Basename.

If a line is empty, Dirname will produce a '.'. Trailing slashes are removed, unless Dirname returns the root folder. The behaviour of Dirname is the same as filepath.Dir (not by coincidence).

Example
package main

import (
	"github.com/bitfield/script"
)

func main() {
	input := []string{
		"",
		"/",
		"/root",
		"/tmp/example.php",
		"/var/tmp/",
		"./src/filters",
		"C:/Program Files",
	}
	script.Slice(input).Dirname().Stdout()
}
Output:

.
/
/
/tmp
/var
./src
C:

func (*Pipe) EachLine added in v0.3.0

func (p *Pipe) EachLine(process func(string, *strings.Builder)) *Pipe

EachLine calls the specified function for each line of input, passing it the line as a string, and a *strings.Builder to write its output to. The return value from EachLine is a pipe containing the contents of the strings.Builder.

Example
package main

import (
	"strings"

	"github.com/bitfield/script"
)

func main() {
	script.File("testdata/test.txt").EachLine(func(line string, out *strings.Builder) {
		out.WriteString("> " + line + "\n")
	}).Stdout()
}
Output:

> This is the first line in the file.
> Hello, world.
> This is another line in the file.

func (*Pipe) Echo added in v0.2.0

func (p *Pipe) Echo(s string) *Pipe

Echo returns a pipe containing the supplied string.

Example
package main

import (
	"github.com/bitfield/script"
)

func main() {
	script.NewPipe().Echo("Hello, world!").Stdout()
}
Output:

Hello, world!

func (*Pipe) Error

func (p *Pipe) Error() error

Error returns the last error returned by any pipe operation, or nil otherwise.

func (*Pipe) Exec added in v0.5.0

func (p *Pipe) Exec(cmdLine string) *Pipe

Exec runs an external command, sending it the contents of the pipe as input, and returns a pipe containing the command's combined output (`stdout` and `stderr`). The effect of this is to use the external command as a filter on the pipe.

If the command had a non-zero exit status, the pipe's error status will also be set to the string "exit status X", where X is the integer exit status.

Example
package main

import (
	"github.com/bitfield/script"
)

func main() {
	script.Echo("Hello, world!").Exec("tr a-z A-Z").Stdout()
}
Output:

HELLO, WORLD!

func (*Pipe) ExecForEach added in v0.15.0

func (p *Pipe) ExecForEach(cmdTpl string) *Pipe

ExecForEach runs the supplied command once for each line of input, and returns a pipe containing the output. The command string is interpreted as a Go template, so `{{.}}` will be replaced with the input value, for example. If any command resulted in a non-zero exit status, the pipe's error status will also be set to the string "exit status X", where X is the integer exit status.

Example
package main

import (
	"github.com/bitfield/script"
)

func main() {
	script.Echo("a\nb\nc\n").ExecForEach("echo {{.}}").Stdout()
}
Output:

a
b
c

func (*Pipe) ExitStatus added in v0.5.0

func (p *Pipe) ExitStatus() int

ExitStatus returns the integer exit status of a previous command, if the pipe's error status is set, and if the error matches the pattern "exit status %d". Otherwise, it returns zero.

Example
package main

import (
	"fmt"

	"github.com/bitfield/script"
)

func main() {
	p := script.Exec("echo")
	fmt.Println(p.ExitStatus())
}
Output:

0

func (*Pipe) First added in v0.9.0

func (p *Pipe) First(lines int) *Pipe

First reads from the pipe, and returns a new pipe containing only the first N lines. If there is an error reading the pipe, the pipe's error status is also set.

Example
package main

import (
	"github.com/bitfield/script"
)

func main() {
	script.Echo("a\nb\nc\n").First(2).Stdout()
}
Output:

a
b

func (*Pipe) Freq added in v0.9.0

func (p *Pipe) Freq() *Pipe

Freq reads from the pipe, and returns a new pipe containing only unique lines from the input, prefixed with a frequency count, in descending numerical order (most frequent lines first). Lines with equal frequency will be sorted alphabetically. If there is an error reading the pipe, the pipe's error status is also set.

This is a common pattern in shell scripts to find the most frequently-occurring lines in a file:

sort testdata/freq.input.txt |uniq -c |sort -rn

Freq's behaviour is like the combination of Unix `sort`, `uniq -c`, and `sort -rn` used here. You can use Freq in combination with First to get, for example, the ten most common lines in a file:

script.Stdin().Freq().First(10).Stdout()

Like `uniq -c`, Freq left-pads its count values if necessary to make them easier to read:

10 apple

4 banana
2 orange
1 kumquat
Example
package main

import (
	"strings"

	"github.com/bitfield/script"
)

func main() {
	input := strings.Join([]string{
		"apple",
		"orange",
		"banana",
		"banana",
		"apple",
		"orange",
		"kumquat",
		"apple",
		"orange",
		"apple",
		"banana",
		"banana",
		"apple",
		"apple",
		"orange",
		"apple",
		"apple",
		"apple",
		"apple",
	}, "\n")
	script.Echo(input).Freq().Stdout()
}
Output:

10 apple
 4 banana
 4 orange
 1 kumquat

func (*Pipe) Join added in v0.7.0

func (p *Pipe) Join() *Pipe

Join reads the contents of the pipe, line by line, and joins them into a single space-separated string. It returns a pipe containing this string. Any terminating newline is preserved.

Example
package main

import (
	"github.com/bitfield/script"
)

func main() {
	script.Echo("hello\nworld\n").Join().Stdout()
}
Output:

hello world

func (*Pipe) Last added in v0.12.0

func (p *Pipe) Last(lines int) *Pipe

Last reads from the pipe, and returns a new pipe containing only the last N lines. If there is an error reading the pipe, the pipe's error status is also set.

Example
package main

import (
	"github.com/bitfield/script"
)

func main() {
	script.Echo("a\nb\nc\n").Last(2).Stdout()
}
Output:

b
c

func (*Pipe) Match added in v0.2.0

func (p *Pipe) Match(s string) *Pipe

Match reads from the pipe, and returns a new pipe containing only lines that contain the specified string. If there is an error reading the pipe, the pipe's error status is also set.

Example
package main

import (
	"github.com/bitfield/script"
)

func main() {
	script.Echo("a\nb\nc\n").Match("b").Stdout()
}
Output:

b

func (*Pipe) MatchRegexp added in v0.3.0

func (p *Pipe) MatchRegexp(re *regexp.Regexp) *Pipe

MatchRegexp reads from the pipe, and returns a new pipe containing only lines that match the specified compiled regular expression. If there is an error reading the pipe, the pipe's error status is also set.

Example
package main

import (
	"regexp"

	"github.com/bitfield/script"
)

func main() {
	re := regexp.MustCompile("w.*d")
	script.Echo("hello\nworld\n").MatchRegexp(re).Stdout()
}
Output:

world

func (*Pipe) Read added in v0.8.1

func (p *Pipe) Read(b []byte) (int, error)

Read reads up to len(b) bytes from the data source into b. It returns the number of bytes read and any error encountered. At end of file, or on a nil pipe, Read returns 0, io.EOF.

Unlike most sinks, Read does not necessarily read the whole contents of the pipe. It will read as many bytes as it takes to fill the slice.

Example
package main

import (
	"fmt"

	"github.com/bitfield/script"
)

func main() {
	buf := make([]byte, 12)
	n, err := script.Echo("hello world\n").Read(buf)
	if err != nil {
		panic(err)
	}
	fmt.Println(n)
}
Output:

12

func (*Pipe) Reject added in v0.2.0

func (p *Pipe) Reject(s string) *Pipe

Reject reads from the pipe, and returns a new pipe containing only lines that do not contain the specified string. If there is an error reading the pipe, the pipe's error status is also set.

Example
package main

import (
	"github.com/bitfield/script"
)

func main() {
	script.Echo("a\nb\nc\n").Reject("b").Stdout()
}
Output:

a
c

func (*Pipe) RejectRegexp added in v0.3.0

func (p *Pipe) RejectRegexp(re *regexp.Regexp) *Pipe

RejectRegexp reads from the pipe, and returns a new pipe containing only lines that don't match the specified compiled regular expression. If there is an error reading the pipe, the pipe's error status is also set.

Example
package main

import (
	"regexp"

	"github.com/bitfield/script"
)

func main() {
	re := regexp.MustCompile("w.*d")
	script.Echo("hello\nworld\n").RejectRegexp(re).Stdout()
}
Output:

hello

func (*Pipe) Replace added in v0.10.0

func (p *Pipe) Replace(search, replace string) *Pipe

Replace filters its input by replacing all occurrences of the string `search` with the string `replace`. If there is an error reading the pipe, the pipe's error status is also set.

Example
package main

import (
	"github.com/bitfield/script"
)

func main() {
	script.Echo("a\nb\nc\n").Replace("b", "replacement").Stdout()
}
Output:

a
replacement
c

func (*Pipe) ReplaceRegexp added in v0.10.0

func (p *Pipe) ReplaceRegexp(re *regexp.Regexp, replace string) *Pipe

ReplaceRegexp filters its input by replacing all matches of the compiled regular expression `re` with the replacement string `replace`. Inside `replace`, $ signs are interpreted as in regexp.Expand, so for instance "$1" represents the text of the first submatch. If there is an error reading the pipe, the pipe's error status is also set.

Example
package main

import (
	"regexp"

	"github.com/bitfield/script"
)

func main() {
	re := regexp.MustCompile("w.*d")
	script.Echo("hello\nworld\n").ReplaceRegexp(re, "replacement").Stdout()
}
Output:

hello
replacement

func (*Pipe) SHA256Sum added in v0.17.0

func (p *Pipe) SHA256Sum() (string, error)

SHA256Sum calculates the SHA-256 of the file from the pipe's reader, and returns the hex-encoded string result, or an error. If there is an error reading the pipe, the pipe's error status is also set.

Example
package main

import (
	"fmt"

	"github.com/bitfield/script"
)

func main() {
	sum, err := script.Echo("hello world").SHA256Sum()
	if err != nil {
		panic(err)
	}
	fmt.Println(sum)
}
Output:

b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9

func (*Pipe) SHA256Sums added in v0.17.0

func (p *Pipe) SHA256Sums() *Pipe

SHA256Sums reads a list of file paths from the pipe, one per line, and returns a pipe that contains the SHA-256 checksum of each pathname, in hex. If there are any errors (for example, non-existent files), the pipe's error status will be set to the first error encountered, but execution will continue.

Example
package main

import (
	"github.com/bitfield/script"
)

func main() {
	script.Echo("testdata/test.txt").SHA256Sums().Stdout()
}
Output:

a562c9c95e2ff3403e7ffcd8508c6b54d47d5f251387758d3e63dbaaa8296341

func (*Pipe) SetError

func (p *Pipe) SetError(err error)

SetError sets the pipe's error status to the specified error.

func (*Pipe) Slice added in v0.18.0

func (p *Pipe) Slice() ([]string, error)

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.

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 that is the empty string.

Example
package main

import (
	"fmt"

	"github.com/bitfield/script"
)

func main() {
	s, err := script.Echo("a\nb\nc\n").Slice()
	if err != nil {
		panic(err)
	}
	fmt.Println(s)
}
Output:

[a b c]

func (*Pipe) Stdout added in v0.6.0

func (p *Pipe) Stdout() (int, error)

Stdout writes the contents of the pipe to its configured 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 error status, Stdout returns zero plus the existing error.

Example
package main

import (
	"fmt"

	"github.com/bitfield/script"
)

func main() {
	n, err := script.Echo("a\nb\nc\n").Stdout()
	if err != nil {
		panic(err)
	}
	fmt.Println(n)
}
Output:

a
b
c
6

func (*Pipe) String

func (p *Pipe) String() (string, error)

String returns the contents of the Pipe as a string, or an error, and closes the pipe after reading. If there is an error reading, the pipe's error status is also set.

Note that String consumes the complete output of the pipe, which closes the input reader automatically. Therefore, calling String (or any other sink method) again on the same pipe will return an error.

Example
package main

import (
	"fmt"

	"github.com/bitfield/script"
)

func main() {
	s, err := script.Echo("hello\nworld").String()
	if err != nil {
		panic(err)
	}
	fmt.Println(s)
}
Output:

hello
world

func (*Pipe) WithError

func (p *Pipe) WithError(err error) *Pipe

WithError sets the pipe's error status to the specified error and returns the modified pipe.

func (*Pipe) WithReader

func (p *Pipe) WithReader(r io.Reader) *Pipe

WithReader takes an io.Reader, and associates the pipe with that reader. If necessary, the reader will be automatically closed once it has been completely read.

func (*Pipe) WithStdout added in v0.18.2

func (p *Pipe) WithStdout(w io.Writer) *Pipe

WithStdout takes an io.Writer, and associates the pipe's standard output with that reader, instead of the default os.Stdout. This is primarily useful for testing.

func (*Pipe) WriteFile added in v0.4.0

func (p *Pipe) WriteFile(fileName string) (int64, error)

WriteFile writes the contents of the Pipe to the specified file, and closes the pipe after reading. If the file already exists, it is truncated and the new data will replace the old. It returns the number of bytes successfully written, or an error. If there is an error reading or writing, the pipe's error status is also set.

type ReadAutoCloser added in v0.8.0

type ReadAutoCloser struct {
	// contains filtered or unexported fields
}

ReadAutoCloser represents a pipe source that will be automatically closed once it has been fully read.

func NewReadAutoCloser added in v0.8.0

func NewReadAutoCloser(r io.Reader) ReadAutoCloser

NewReadAutoCloser returns an ReadAutoCloser wrapping the supplied Reader. If the Reader is not a Closer, it will be wrapped in a NopCloser to make it closable.

func (ReadAutoCloser) Close added in v0.8.0

func (a ReadAutoCloser) Close() error

Close closes the data source associated with a, and returns the result of that close operation.

func (ReadAutoCloser) Read added in v0.8.0

func (a ReadAutoCloser) Read(buf []byte) (n int, err error)

Read reads up to len(buf) bytes from the data source into buf. It returns the number of bytes read and any error encountered. At end of file, Read returns 0, io.EOF. In the EOF case, the data source will be closed.

Directories

Path Synopsis
examples
cat
ls
visitors
This program reads an Apache logfile in Common Log Format, like this: 212.205.21.11 - - [30/Jun/2019:17:06:15 +0000] "GET / HTTP/1.1" 200 2028 "https://example.com/ "Mozilla/5.0 (Linux; Android 8.0.0; FIG-LX1 Build/HUAWEIFIG-LX1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.156 Mobile Safari/537.36" It extracts the first column of each line (the visitor IP address), counts the frequency of each unique IP address in the log, and outputs the 10 most frequent visitors in the log.
This program reads an Apache logfile in Common Log Format, like this: 212.205.21.11 - - [30/Jun/2019:17:06:15 +0000] "GET / HTTP/1.1" 200 2028 "https://example.com/ "Mozilla/5.0 (Linux; Android 8.0.0; FIG-LX1 Build/HUAWEIFIG-LX1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.156 Mobile Safari/537.36" It extracts the first column of each line (the visitor IP address), counts the frequency of each unique IP address in the log, and outputs the 10 most frequent visitors in the log.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL