sh

package module
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: Feb 21, 2025 License: Apache-2.0 Imports: 14 Imported by: 36

README

CI PkgGoDev

go-sh

install: go get gomodules.xyz/go-sh

Pipe Example:

package main

import "gomodules.xyz/go-sh"

func main() {
	sh.Command("echo", "hello\tworld").Command("cut", "-f2").Run()
}

Because I like os/exec, go-sh is very much modelled after it. However, go-sh provides a better experience.

These are some of its features:

  • keep the variable environment (e.g. export)
  • alias support (e.g. alias in shell)
  • remember current dir
  • pipe command
  • shell build-in commands echo & test
  • timeout support
  • run multiple concurrent leaf commands using a single pipe input

Examples are important:

sh: echo hello
go: sh.Command("echo", "hello").Run()

sh: export BUILD_ID=123
go: s = sh.NewSession().SetEnv("BUILD_ID", "123")

sh: alias ll='ls -l'
go: s = sh.NewSession().Alias('ll', 'ls', '-l')

sh: (cd /; pwd)
go: sh.Command("pwd", sh.Dir("/")).Run()

sh: test -d data || mkdir data
go: if ! sh.Test("dir", "data") { sh.Command("mkdir", "data").Run() }

sh: cat first second | awk '{print $1}'
go: sh.Command("cat", "first", "second").Command("awk", "{print $1}").Run()

sh: count=$(echo "one two three" | wc -w)
go: count, err := sh.Echo("one two three").Command("wc", "-w").Output()

sh(in ubuntu): timeout 1s sleep 3
go: c := sh.Command("sleep", "3"); c.Start(); c.WaitTimeout(time.Second) # default SIGKILL
go: out, err := sh.Command("sleep", "3").SetTimeout(time.Second).Output() # set session timeout and get output)

sh: echo hello | cat
go: out, err := sh.Command("cat").SetInput("hello").Output()

sh: cat # read from stdin
go: out, err := sh.Command("cat").SetStdin(os.Stdin).Output()

sh: ls -l > /tmp/listing.txt # write stdout to file
go: err := sh.Command("ls", "-l").WriteStdout("/tmp/listing.txt")

If you need to keep env and dir, it is better to create a session

session := sh.NewSession()
session.SetEnv("BUILD_ID", "123")
session.SetDir("/")
# then call cmd
session.Command("echo", "hello").Run()
# set ShowCMD to true for easily debug
session.ShowCMD = true

By default, pipeline returns error only if the last command exit with a non-zero status. However, you can also enable pipefail option like bash. In that case, pipeline returns error if any of the commands fail and for multiple failed commands, it returns the error of rightmost failed command.

session := sh.NewSession()
session.PipeFail = true
session.Command("cat", "unknown-file").Command("echo").Run()

By default, pipelines's std-error is set to last command's std-error. However, you can also combine std-errors of all commands into pipeline's std-error using session.PipeStdErrors = true.

By default, pipeline returns error only if the last command exit with a non-zero status. However, you can also enable pipefail option like bash. In that case, pipeline returns error if any of the commands fail and for multiple failed commands, it returns the error of rightmost failed command.

session := sh.NewSession()
session.PipeFail = true
session.Command("cat", "unknown-file").Command("echo").Run()

Designing a Command Chain to Run Multiple Concurrent Leaf Commands Using a Single Pipe Input.

Features be like:

  • Input Sharing: All leaf commands take the same input from a pipe.
  • Separate Environments: Each leaf command runs with its own environment variables.
  • Output Aggregation: Outputs from all commands are combined into a single result.
  • Error Handling: Errors are collected and included in the output (e.g., shell or variable).
  • Timeouts: Each command has same timeout and will apply simultaneously.

Below is an example of multiple concurrent leaf commands using a single pipe input

s := sh.NewSession()
s.ShowCMD = true
s.Command("echo", "hello world").LeafCommand("xargs").LeafCommand("xargs")
s.Run()

Below is an example of each leaf command runs with its own environment variables

s := sh.NewSession()
s.ShowCMD = true
var args1,args2 []interface{}

mp := make(map[string]string)
mp["COMPANY_NAME"] = "APPSCODE"
args1 = append(args1, "COMPANY_NAME")
args1 = append(args1, mp)
s.LeafCommand("printenv", args1...)

mp["COMPANY_NAME"] = "GOOGLE"
args2 = append(args2, "COMPANY_NAME")
args2 = append(args2, mp)
s.LeafCommand("printenv", args2...)

s.Run()

for more information, it better to see docs. Go Walker

contribute

If you love this project, starring it will encourage the coder. Pull requests are welcome.

support the author: alipay

thanks

this project is based on http://github.com/codegangsta/inject. thanks for the author.

the reason to use Go shell

Sometimes we need to write shell scripts, but shell scripts are not good at working cross platform, Go, on the other hand, is good at that. Is there a good way to use Go to write shell like scripts? Using go-sh we can do this now.

Documentation

Overview

Package go-sh is intended to make shell call with golang more easily. Some usage is more similar to os/exec, eg: Run(), Output(), Command(name, args...)

But with these similar function, pipe is added in and this package also got shell-session support.

Why I love golang so much, because the usage of golang is simple, but the power is unlimited. I want to make this pakcage got the sample style like golang.

// just like os/exec
sh.Command("echo", "hello").Run()

// support pipe
sh.Command("echo", "hello").Command("wc", "-c").Run()

// create a session to store dir and env
sh.NewSession().SetDir("/").Command("pwd")

// shell buildin command - "test"
sh.Test("dir", "mydir")

// like shell call: (cd /; pwd)
sh.Command("pwd", sh.Dir("/")) same with sh.Command(sh.Dir("/"), "pwd")

// output to json and xml easily
v := map[string] int {}
err = sh.Command("echo", `{"number": 1}`).UnmarshalJSON(&v)
Example (Command_Pipe)
out, err := sh.Command("echo", "-n", "hi").Command("wc", "-c").Output()
fmt.Println(string(out), err)
Output:

Example (Command_SetDir)
out, err := sh.Command("pwd", sh.Dir("/")).Output()
fmt.Println(string(out), err)
Output:

Example (Session_Command)

#!/bin/bash - # export PATH=/usr/bin:/bin alias ll='ls -l' cd /usr if test -d "local" then

ll local | awk '{print $1, $NF}' | grep bin

fi

s := NewSession()
//s.ShowCMD = true
s.Env["PATH"] = "/usr/bin:/bin"
s.SetDir("/bin")
s.Alias("ll", "ls", "-l")

if s.Test("d", "local") {
	//s.Command("ll", []string{"local"}).Command("awk", []string{"{print $1, $NF}"}).Command("grep", []string{"bin"}).Run()
	s.Command("ll", "local").Command("awk", "{print $1, $NF}").Command("grep", "bin").Run()
}
Output:

Index

Examples

Constants

This section is empty.

Variables

View Source
var ErrExecTimeout = errors.New("execute timeout")

Functions

func Go

func Go(f func() error) chan error

func Test

func Test(exp string, arg string) bool

expression can be d,dir, f,file, link

Example
if sh.Test("dir", "mydir") {
	fmt.Println("mydir exists")
}
Output:

Types

type Dir

type Dir string

type Session

type Session struct {
	Env     map[string]string
	Stdin   io.Reader
	Stdout  io.Writer
	Stderr  io.Writer
	ShowCMD bool // enable for debug

	// additional pipe options
	PipeFail      bool // returns error of rightmost no-zero command
	PipeStdErrors bool // combine std errors of all pipe commands
	// contains filtered or unexported fields
}

func Command

func Command(name string, a ...interface{}) *Session
Example
out, err := sh.Command("echo", "hello").Output()
fmt.Println(string(out), err)
Output:

func Echo

func Echo(in string) *Session

func InteractiveSession

func InteractiveSession() *Session

func NewSession

func NewSession() *Session

func (*Session) Alias

func (s *Session) Alias(alias, cmd string, args ...string)
Example
s := NewSession()
s.Alias("alias_echo_hello", "echo", "hello")
out, err := s.Command("alias_echo_hello", "world").Output()
if err != nil {
	log.Fatal(err)
}
fmt.Println(string(out))
Output:

hello world

func (*Session) AppendStdout

func (s *Session) AppendStdout(f string) error

func (*Session) Call

func (s *Session) Call(name string, a ...interface{}) error

combine Command and Run

func (*Session) CombinedOutput

func (s *Session) CombinedOutput() (out []byte, err error)

func (*Session) Command

func (s *Session) Command(name string, a ...interface{}) *Session
Example
s := NewSession()
out, err := s.Command("echo", "hello").Output()
if err != nil {
	log.Fatal(err)
}
fmt.Println(string(out))
Output:

hello
Example (Pipe)
s := NewSession()
out, err := s.Command("echo", "hello", "world").Command("awk", "{print $2}").Output()
if err != nil {
	log.Fatal(err)
}
fmt.Println(string(out))
Output:

world

func (*Session) Getwd

func (s *Session) Getwd() string

func (*Session) Kill

func (s *Session) Kill(sig os.Signal)

func (*Session) LeafCommand added in v0.2.0

func (s *Session) LeafCommand(name string, a ...interface{}) *Session

func (*Session) Output

func (s *Session) Output() (out []byte, err error)

func (*Session) Run

func (s *Session) Run() (err error)

func (*Session) SetDir

func (s *Session) SetDir(dir string) *Session

func (*Session) SetEnv

func (s *Session) SetEnv(key, value string) *Session

func (*Session) SetInput

func (s *Session) SetInput(in string) *Session

func (*Session) SetStdin

func (s *Session) SetStdin(r io.Reader) *Session

func (*Session) SetTimeout

func (s *Session) SetTimeout(d time.Duration) *Session

func (*Session) Start

func (s *Session) Start() (err error)

start command

func (*Session) Test

func (s *Session) Test(expression string, argument string) bool

expression can be dir, file, link

func (*Session) UnmarshalJSON

func (s *Session) UnmarshalJSON(data interface{}) (err error)

unmarshal shell output to decode json

func (*Session) UnmarshalXML

func (s *Session) UnmarshalXML(data interface{}) (err error)

unmarshal command output into xml

func (*Session) Wait

func (s *Session) Wait() error

Should be call after Start() only catch the last command error

func (*Session) WaitTimeout

func (s *Session) WaitTimeout(timeout time.Duration) (err error)

func (*Session) WriteStdout

func (s *Session) WriteStdout(f string) error

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL
JackTT - Gopher 🇻🇳