shell

package module
v0.0.0-...-a6be64f Latest Latest
Warning

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

Go to latest
Published: Mar 18, 2022 License: MIT Imports: 8 Imported by: 0

README

go-shell

GitHub release license Build Status

Library to write "shelling out" Go code more shell-like, while remaining idiomatic to Go.

Features

  • Function-wrapper factories for shell commands
  • Panic on non-zero exits for set -e behavior
  • Result of Run() is a Stringer for STDOUT, has Error for STDERR
  • Heavily variadic function API Cmd("rm", "-r", "foo") == Cmd("rm -r", "foo")
  • Go-native piping Cmd(...).Pipe(...) or inline piping Cmd("... | ...")
  • Template compatible "last arg" piping Cmd(..., Cmd(..., Cmd(...)))
  • Optional trace output mode like set +x
  • Similar variadic functions for paths and path templates
  • CommandBuilder for creating command using SSH, Docker or Docker over SSH

Docker support (commandbuilder)

Running commands inside docker containers can be used by the CommandBuilder (see examples below). The variable connection.Docker has to set to the docker container id or the following schema has to be used:

CONTAINER is the name of the docker-compose container.

DSN style configuration Description
compose:CONTAINER Lookup container id using docker-compose in current directory
compose:CONTAINER;path=/path/to/project Lookup container id using docker-compose in /path/to/project directory
compose:CONTAINER;path=/path/to/project;file=custom-compose.yml Lookup container id using docker-compose in /path/to/project directory and custom-compose.yml file
compose:CONTAINER;project-name=foobar Lookup container id using docker-compose in current directory with project name foobar
compose:CONTAINER;host=example.com Lookup container id using docker-compose in current directory with docker host example.com
compose:CONTAINER;env[FOOBAR]=BARFOO Lookup container id using docker-compose in current directory with env var FOOBAR set to BARFOO
Query style configuration Description
compose://CONTAINER Lookup container id using docker-compose in current directory
compose://CONTAINER?path=/path/to/project Lookup container id using docker-compose in /path/to/project directory
compose://CONTAINER?path=/path/to/project&file=custom-compose.yml Lookup container id using docker-compose in /path/to/project directory and custom-compose.yml file
compose://CONTAINER?project-name=foobar Lookup container id using docker-compose in current directory with project name foobar
compose://CONTAINER?host=example.com Lookup container id using docker-compose in current directory with docker host example.com
compose://CONTAINER?env[FOOBAR]=BARFOO Lookup container id using docker-compose in current directory with env var FOOBAR set to BARFOO

Examples shell

import (
  "fmt"
  "github.com/webdevops/go-shell"
)

var (
  sh = shell.Run
)

shell.Trace = true // like set +x
shell.Shell = []string{"/bin/bash", "-c"} // defaults to /bin/sh

func main() {
  defer shell.ErrExit()
  sh("echo Foobar > /foobar")
  sh("rm /fobar") // typo raises error
  sh("echo Done!") // never run, program exited
}
import (
  "fmt"
  "github.com/webdevops/go-shell"
)

func main() {
  cmd := shell.Cmd("echo", "foobar").Pipe("wc", "-c").Pipe("awk", "'{print $1}'")
  
  // -> wc -c | awk '{print $1}'
  fmt.Println(cmd.ToString())
  
  // -> 7 
  fmt.Println(cmd.Run())
}
import "github.com/webdevops/go-shell"

var (
  echo = shell.Cmd("echo").OutputFn()
  copy = shell.Cmd("cp").ErrFn()
  rm = shell.Cmd("rm").ErrFn()
)

func main() {
  err := copy("/foo", "/bar")
  // handle err
  err = rm("/bar")
  // handle err
  out, _ := echo("Done!")
}

Error recovery

package main

import (
	"os"
	"fmt"
	"github.com/webdevops/go-shell"
)

func main() {
	defer func() {
		if r := recover(); r != nil {
			message := fmt.Sprintf("%v", r)

			if obj, ok := r.(*shell.Process); ok {
				message = obj.Debug()
			}
			fmt.Println(message)
			os.Exit(255)
		}
	}()

	shell.Cmd("exit", "2").Run()
}

Examples commandbuilder

package main

import (
	"fmt"
	"github.com/webdevops/go-shell"
	"github.com/webdevops/go-shell/commandbuilder"
)

func main() {
	var cmd *shell.Command
	var connection commandbuilder.Connection

	// ------------------------------------------
	// local execution
	connection = commandbuilder.Connection{}
		
	cmd = shell.Cmd(connection.CommandBuilder("date")...)
	cmd.Run()

	// ------------------------------------------
	// SSH access
	connection = commandbuilder.Connection{}
	connection.Ssh.Set("foobar@example.com")
	
	cmd = shell.Cmd(connection.CommandBuilder("date")...)
	fmt.Println("LOCAL: " + cmd.Run().Stdout.String())
	
	
	// ------------------------------------------
	// Docker execution
	connection = commandbuilder.Connection{}
	connection.Docker.Set("32ceb49d2958")
	
	cmd = shell.Cmd(connection.CommandBuilder("date")...)
	fmt.Println("DOCKER: " + cmd.Run().Stdout.String())

	// ------------------------------------------
	// Docker (lookup via docker-compose) execution
	connection = commandbuilder.Connection{}
	connection.Docker.Set("compose:mysql;path=/path/to/project;file=custom-compose-yml")

	cmd = shell.Cmd(connection.CommandBuilder("date")...)
	fmt.Println("DOCKER with COMPOSE: " + cmd.Run().Stdout.String())

	// ------------------------------------------
	// Docker on remote host via SSH execution
	connection = commandbuilder.Connection{}
	connection.Ssh.Set("foobar@example.com")
	connection.Docker.Set("32ceb49d2958")
	
	cmd = shell.Cmd(connection.CommandBuilder("date")...)
	fmt.Println("DOCKER via SSH: " + cmd.Run().Stdout.String())
}

License

MIT

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	// Available shell list
	ShellList = map[string][]string{
		"zsh":         {"/bin/zsh", "-o", "errexit", "-o", "pipefail", "-c"},
		"bash":        {"/bin/bash", "-o", "errexit", "-o", "pipefail", "-c"},
		"termux-bash": {"/data/data/com.termux/files/usr/bin/bash", "-o", "errexit", "-o", "pipefail", "-c"},
		"sh":          {"/bin/sh", "-o", "errexit", "-c"},
	}

	// Shell for command invocation
	Shell = []string{"/data/data/com.termux/files/usr/bin/bash", "-o", "errexit", "-c"}

	// Specifies if panic is thrown if command fails
	Panic = true

	// Error func hook if command fails
	ErrorFunc = func(c *Command, p *Process) {}

	// Verbose func hook before command is executed
	VerboseFunc = func(c *Command) {}

	// Trace command for each executed
	Trace = false

	// Trae output prefix
	TracePrefix = "+"
)

Functions

func ErrExit

func ErrExit()

func Path

func Path(parts ...string) string

func PathTemplate

func PathTemplate(parts ...string) func(...interface{}) string

func Quote

func Quote(arg string) string

Quote shell arguments as string

func QuoteValues

func QuoteValues(arg ...string) []string

Quote multiple shell arguments as string list

func SetDefaultShell

func SetDefaultShell(shell string)

Sets the default shell (eg. "sh" or "bash") for command execution and usage in ShellCommandBuilder

Types

type Command

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

func Cmd

func Cmd(cmd ...interface{}) *Command

Create new Command instance

func NewCmd

func NewCmd(command string, args ...string) *Command

Create a new shell command instance using strings as parameters

func (*Command) ErrFn

func (c *Command) ErrFn() func(...interface{}) error

func (*Command) OutputFn

func (c *Command) OutputFn() func(...interface{}) (string, error)

func (*Command) Pipe

func (c *Command) Pipe(cmd ...interface{}) *Command

Add command as pipe to an existing command

func (*Command) ProcFn

func (c *Command) ProcFn() func(...interface{}) *Process

func (*Command) Run

func (c *Command) Run() *Process

Run command

func (*Command) RunInteractive

func (c *Command) RunInteractive() *Process

Run command in interactive mode

func (*Command) ToString

func (c *Command) ToString() string

Create human readable command as string

type Process

type Process struct {
	Stdout     *bytes.Buffer
	Stderr     *bytes.Buffer
	Stdin      io.WriteCloser
	ExitStatus int
	Command    *Command
}

func Run

func Run(cmd ...interface{}) *Process

func (*Process) Bytes

func (p *Process) Bytes() []byte

func (*Process) Debug

func (p *Process) Debug() string

Create human readable representation of process status

func (*Process) Error

func (p *Process) Error() error

func (*Process) Read

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

func (*Process) String

func (p *Process) String() string

func (*Process) Write

func (p *Process) Write(b []byte) (int, error)

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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