forked from natefinch/sh
/
sh.go
128 lines (116 loc) · 4.43 KB
/
sh.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
// Package sh is intended to make working with shell commands more shell-like.
// This package is basically just syntactic sugar wrapped around
// labix.org/v2/pipe, which in turn just wraps os/exec, but it can make your
// code a lot easier to read when you have a simple shell-like line rather than
// a huge mess of pipes and commands and conversions etc.
//
// // create functions that runs the echo, grep, and wc shell commands
// echo := sh.Cmd("echo")
// grep := sh.Cmd("grep")
// wc := sh.Cmd("wc")
//
// // run echo, pipe the output through grep and then through wc
// // effectively the same as
// // $ echo Hi there! | grep -o Hi | wc -w
// fmt.Print(sh.Pipe(echo("Hi there!"), grep("-o", "Hi"), wc("-w")))
//
// // output:
// // 1
package sh
import (
"io"
"strings"
"labix.org/v2/pipe"
)
// Cmd returns a function that will return an Executable for the given command
// with the given args. This is a convenience for defining functions for
// commonly reused Executables, such as grep, ls, mkdir, etc.
//
// The args that are passed to Cmd are passed to the Executable when the
// returned function is run, allowing you to pre-set some common arguments.
func Cmd(name string, args0 ...string) func(args ...string) Executable {
return func(args1 ...string) Executable {
return Executable{pipe.Exec(name, append(args0, args1...)...)}
}
}
// Runner returns a function that will run the given shell command with
// specified arguments. This is a convenience for creating one-off commands that
// aren't going to be put into Pipe, but instead just run standalone. The
// return values are the combined output and an error if any.
//
// The args that are passed to Runner are passed to the shell command when the
// returned function is run, allowing you to pre-set some common arguments.
func Runner(name string, args0 ...string) func(args ...string) (string, error) {
return func(args1 ...string) (string, error) {
return Executable{pipe.Exec(name, append(args0, args1...)...)}.Run()
}
}
// Dump returns an excutable that will read the given file and dump its contents
// as the Executable's stdout.
func Dump(filename string) Executable {
return Executable{pipe.ReadFile(filename)}
}
// Read returns an executable that will read from the given reader and use it as
// the Executable's stdout.
func Read(r io.Reader) Executable {
return Executable{pipe.Read(r)}
}
// Pipe connects the output of one Executable to the input of the next
// Executable in the list. The result is an Executable that, when run, returns
// the output of the last Executable run, and any error it might have had.
//
// If any of the Executables fails, no further Executables are run, and the
// failing Executable's stderr and error are returned.
func Pipe(cmds ...Executable) Executable {
ps := make([]pipe.Pipe, len(cmds))
for i, c := range cmds {
ps[i] = c.Pipe
}
return Executable{pipe.Line(ps...)}
}
// PipeWith functions like Pipe, but runs the first command with stdin as the
// input.
func PipeWith(stdin string, cmds ...Executable) Executable {
ps := make([]pipe.Pipe, len(cmds)+1)
ps[0] = pipe.Read(strings.NewReader(stdin))
for i, c := range cmds {
ps[i+1] = c.Pipe
}
return Executable{pipe.Line(ps...)}
}
// Executable is a runnable construct. You can run it by calling Run(), or by
// calling String() (which is automatically done when passing it into a
// fmt.Print style function). It can be passed into Pipe to form a chain of
// Executables that are executed in series.
type Executable struct {
pipe.Pipe
}
// RunWith executes the command with the given string as standard input, and
// returns stdout and a nil error on success, or stderr and a non-nil error on
// failure.
func (c Executable) RunWith(stdin string) (string, error) {
out, err := pipe.CombinedOutput(
pipe.Line(pipe.Read(strings.NewReader(stdin)), c.Pipe),
)
return string(out), err
}
// Run executes the command and returns the combined stdout and stderr, and the
// error if any.
func (c Executable) Run() (string, error) {
out, err := pipe.CombinedOutput(c.Pipe)
return string(out), err
}
// String runs the Executable and returns the standard output if the command
// succeeds, or stderr if the command fails. If stderr is empty on failure, the
// Error() value of the error is returned. This is most useful for passing an
// executable into a fmt.Print style function.
func (c Executable) String() string {
s, err := c.Run()
if err == nil {
return s
}
if s != "" {
return s
}
return err.Error()
}