execmd

package module
v0.4.4 Latest Latest
Warning

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

Go to latest
Published: Apr 18, 2023 License: MIT Imports: 11 Imported by: 1

README

execmd

ExecCmd is a Go package that offers a simplified interface for shell command execution. Built on top of the exec, ExecCmd enables command invocation in a system shell and combines multiple stdout and stderr into a single stdout with prefixes. It supports both local and remote command execution, with remote commands implemented through the OpenSSH binary.

Key features

  • Execute commands in the system shell
  • Execute remote shell commands using OpenSSH binary
  • Capture outputs for programmatic access
  • Real-time stdout and stderr output featuring auto coloring and prefixing
  • Utilize shell variables, pipes, and redirections
  • Compatibility with system SSH configuration (including ssh-agent forwarding)
  • Run commands on multiple remote hosts (ideal for cluster operations) with parallel or serial execution options
  • Minimum number of third party dependencies

Installation

go get "github.com/mikhae1/execmd"

Then import exec-cmd in your application:

import "github.com/mikhae1/execmd"

Examples

Local command execution
package main

import "github.com/mikhae1/execmd"

func main() {
  // run local command in a shell
  execmd.NewCmd().Run("ps aux | grep go")
}
Remote command execution
package main

import "github.com/mikhae1/execmd"

func main() {
  // run command on a remote host using ssh
  remote := execmd.NewSSHCmd("192.168.1.194")
  res, err := remote.Run(`VAR="$(hostname)"; echo "hello $VAR"`)

  if err == nil {
    fmt.Printf("captured output: %s", res.Stdout)
  }
}

Results:

$ /usr/bin/ssh 192.168.1.194 'VAR="$(hostname)"; echo "hello $VAR"'
192.168.1.194 hello host-01.local
captured output: hello host-01.local
Remote cluster command execution
cluster := execmd.NewClusterSSHCmd([]string{"host-01", "host-02", "host-03"})

// execute in parallel order
res, err := cluster.Run(`VAR=std; echo "Hello $VAR out"; echo Hello $VAR err >&2`)

// execute in serial order
res, err = cluster.RunOneByOne(`VAR=std; echo "Hello $VAR out"`)

Parallel execution results:

$ /usr/bin/ssh host-01 'VAR=std; echo "Hello $VAR out"; echo "Hello $VAR err" >&2'
$ /usr/bin/ssh host-02 'VAR=std; echo "Hello $VAR out"; echo "Hello $VAR err" >&2'
$ /usr/bin/ssh host-03 'VAR=std; echo "Hello $VAR out"; echo "Hello $VAR err" >&2'
host-01 Hello std out
host-01@err Hello std err
host-03@err Hello std err
host-03 Hello std out
host-02 Hello std out
host-02@err Hello std err

Serial execution results:

$ /usr/bin/ssh host-01 'VAR=std; echo "Hello $VAR out"; echo Hello $VAR err >&2'
host-01 Hello std out
host-01@err Hello std err
$ /usr/bin/ssh host-02 'VAR=std; echo "Hello $VAR out"; echo Hello $VAR err >&2'
host-02@err Hello std err
host-02 Hello std out
$ /usr/bin/ssh host-03 'VAR=std; echo "Hello $VAR out"; echo Hello $VAR err >&2'
host-03@err Hello std err
host-03 Hello std out

Testing

You should enable SSH server locally and add your personal ssh key to known_hosts to avoid password prompting:

ssh-copy-id 127.0.0.1
ssh-copy-id localhost

Run tests:

go test

License

The MIT License (MIT) - see LICENSE for details.

Documentation

Overview

Package execmd provides a wrapper around https://golang.org/pkg/os/exec/ to execute commands in a shell, pipe stdout and stderr to the console with prefixes, and record output buffers.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type ClusterCmd

type ClusterCmd struct {
	SSHCmd SSHCmd

	Host string
}

ClusterCmd wraps SSHCmd and preserves the host name, and saves errors from .Start() for the .Wait() method.

type ClusterRes

type ClusterRes struct {
	Host string
	Err  error
	Res  CmdRes
}

ClusterRes contains the results of the command execution.

type ClusterSSHCmd

type ClusterSSHCmd struct {
	Cmds   []ClusterCmd
	Errors []error

	Cwd         string
	StopOnError bool
}

ClusterSSHCmd is a wrapper on SSHCmd that allows executing commands on multiple hosts in parallel or sequentially.

func NewClusterSSHCmd

func NewClusterSSHCmd(hosts []string) *ClusterSSHCmd

NewClusterSSHCmd initializes ClusterSSHCmd with defaults.

func (*ClusterSSHCmd) Run

func (c *ClusterSSHCmd) Run(command string, timeout ...time.Duration) (results []ClusterRes, err error)

Run executes a command in parallel on all hosts and waits for the results. The command starts simultaneously on each host. It returns results and the first caught error. To see underlying SSHCmd command errors, access the .Cmds attribute.

func (*ClusterSSHCmd) RunOneByOne

func (c *ClusterSSHCmd) RunOneByOne(command string, timeout ...time.Duration) (results []ClusterRes, err error)

RunOneByOne executes a command in series: run at the first host, then run at the second host, and so on. It returns results and the first caught error. To see underlying SSHCmd command errors, access the .Cmds attribute.

func (*ClusterSSHCmd) Start

func (c *ClusterSSHCmd) Start(command string, timeout ...time.Duration) (results []ClusterRes, err error)

Start executes a command in parallel on all hosts without waiting for the results. The command starts simultaneously on each host. It returns results and the first caught error.

func (*ClusterSSHCmd) Wait

func (c *ClusterSSHCmd) Wait() error

Wait calls SSHCmd.Wait for each Cmd in the list of ClusterCmds. It returns the first caught .Wait() error ans stops if .StopOnError is true. To see underlying SSHCmd command errors, access the .Cmds attribute.

type Cmd

type Cmd struct {
	ShellPath    string
	Interactive  bool
	LoginShell   bool
	RecordStdout bool
	RecordStderr bool
	MuteStdout   bool
	MuteStderr   bool
	MuteCmd      bool
	PrefixStdout string
	PrefixStderr string
	PrefixCmd    string
	CancelFunc   context.CancelFunc

	Cmd *exec.Cmd
}

Cmd is a wrapper struct around exec.Cmd that provides additional functionality such as recording and muting stdout and stderr, and customizing output prefixes.

func NewCmd

func NewCmd() *Cmd

NewCmd initializes a Cmd with default settings.

func (*Cmd) Run

func (c *Cmd) Run(command string, timeout ...time.Duration) (CmdRes, error)

Run is exec.Run() wrapper: runs command and blocks until it finishes, with an optional timeout

func (*Cmd) Start

func (c *Cmd) Start(command string, timeout ...time.Duration) (CmdRes, error)

Start initializes the system shell and output buffers, and starts the command.

func (*Cmd) Wait

func (c *Cmd) Wait() error

Wait wraps exec.Wait() and ensures that the buffers are flushed after waiting.

type CmdRes

type CmdRes struct {
	Stdout *bytes.Buffer
	Stderr *bytes.Buffer
}

CmdRes represents the result of a command, including the stdout and stderr buffers.

type SSHCmd

type SSHCmd struct {
	Cmd           *Cmd
	Interactive   bool
	SSHExecutable string
	Host          string
	User          string
	Port          string
	KeyPath       string
	Cwd           string
}

SSHCmd is a wrapper on Cmd to invoke ssh commands via OpenSSH binary

func NewSSHCmd

func NewSSHCmd(host string) *SSHCmd

NewSSHCmd initializes SSHCmd with defaults and sets the target host

func (*SSHCmd) Run

func (s *SSHCmd) Run(command string, timeout ...time.Duration) (res CmdRes, err error)

Run wraps Cmd.Run(), executing the remote command and waiting for it to complete

func (*SSHCmd) Start

func (s *SSHCmd) Start(command string, timeout ...time.Duration) (res CmdRes, err error)

Start wraps Cmd.Start() with ssh invocation, starting the remote command

func (*SSHCmd) Wait

func (s *SSHCmd) Wait() error

Wait wraps Cmd.Wait(), waiting for the remote command to complete

Jump to

Keyboard shortcuts

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