https://github.com/ess/spawning
A package for spawning shell commands
https://github.com/ess/spawning
Last synced: 8 months ago
JSON representation
A package for spawning shell commands
- Host: GitHub
- URL: https://github.com/ess/spawning
- Owner: ess
- License: apache-2.0
- Created: 2017-03-30T23:26:26.000Z (about 9 years ago)
- Default Branch: develop
- Last Pushed: 2017-04-04T04:57:09.000Z (about 9 years ago)
- Last Synced: 2025-02-22T21:41:54.403Z (over 1 year ago)
- Language: Go
- Homepage:
- Size: 978 KB
- Stars: 2
- Watchers: 2
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
The `spawning` package provides a handy API for spawning shell commands on
UNIXish environments.
## Installation ##
I suggest the use of [glide](https://glide.sh) for managing your Go deps, but
you should be able to install it directly without much issue:
```
go get github.com/ess/spawning
```
As mentioned, though, a better idea is to use glide (or another package manager
that supports SemVer).
## Usage ##
Commands (run via `bash -c`) can be spawned in a one-off manner, or one can
add a collection of commands to a Pool, then run the pool either sequentially,
concurrently, or with a custom Runner.
### Spawning One-off Commands ###
Running a one-off command is pretty easy, really:
```go
package main
import (
"fmt"
"github.com/ess/spawning"
)
func main() {
result := spawning.Run("echo 'My sausages turned to gold!'")
if result.Success {
fmt.Println("The sausages were successfully turned to gold.")
}
}
```
### Spawning Multiple Commands ###
Running multiple commands is fairly easy, too, and you can execute them either
Sequentially, Concurrently, or with a custom Runner implementation.
#### Sequentially ####
Effectively, the sequential runner could be used to create minimal shell
scripts. The important caveat there is that as every command is executed via
`bash -c`, there is no shared environment between the commands in a given Pool.
```go
package main
import (
"fmt"
"github.com/ess/spawning"
)
func main() {
script := spawning.NewPool()
script.Add("mkdir -p output")
script.Add("date > output/begin")
script.Add(`echo "The operation has succeeded" > output/result`)
script.Add("date > output/end")
script.Add(`for step in begin result end ; do cat output/${step} ; done`)
// Run the commands sequentially and process the results
for _, result := range script.Run(spawning.Sequentially()) {
processResult(result)
}
}
func processResult(result *spawning.Result) {
if !result.Success {
fmt.Println("The following command failed:", result.Command)
return
}
fmt.Println(result.Output)
}
```
#### Concurrently ####
Running commands concurrently isn't at all great for running locally, but it is
quite handy for, say, controlling a cluster via SSH.
```go
package main
import (
"fmt"
"github.com/ess/spawning"
)
func main() {
logins := []string{
"joe@192.168.1.1",
"joe@192.168.1.2",
"joe@192.168.1.3",
"jim@10.1.1.1",
}
pool := spawning.NewPool()
for _, login := range logins {
pool.Add(sshCommand(login, "date"))
}
// Run the commands concurrently and process the results
for _, result := range pool.Run(spawning.Concurrently()) {
processResult(result)
}
}
func processResult(result *spawning.Result) {
if !result.Success {
fmt.Println("The following command failed:", result.Command)
return
}
fmt.Println(result.Output)
}
func sshCommand(login string, command string) string {
return fmt.Sprintf("ssh %s '$s'", login, command)
}
```
#### Running With A Custom Runner ####
`spawning.Pool`'s `Run()` method requires that one pass in a `Runner`. If
neither `Sequentially` nor `Concurrently` are suitable for your purposes, you
can implement your own Runner:
```go
package main
import (
"fmt"
"github.com/ess/spawning"
)
type RoadRunner struct{}
func (runner *RoadRunner) Run(commands []string) []*spawning.Result {
results := make([]*spawning.Result, 0)
for _, command := range commands {
results = append(
results,
&spawning.Result{Command: command, Output: "meep meep", Success: true},
)
}
return results
}
func main() {
runner := &RoadRunner{}
for _, result := range spawning.NewPool().Add("sudo ls -lah /").Run(runner) {
processResult(result)
}
}
func processResult(result *spawning.Result) {
if !result.Success {
fmt.Println("The following command failed:", result.Command)
return
}
fmt.Println(result.Output)
}
```
## History ##
* v0.1.1 - I'm a jerk that forgets to include a license
* v0.1.0 - Initial release