mirror of
https://github.com/zyedidia/micro.git
synced 2025-06-18 23:05:40 -04:00
180 lines
4.4 KiB
Go
180 lines
4.4 KiB
Go
package shell
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"os/exec"
|
|
"os/signal"
|
|
|
|
shellquote "github.com/kballard/go-shellquote"
|
|
"github.com/zyedidia/micro/v2/internal/screen"
|
|
"github.com/zyedidia/micro/v2/internal/util"
|
|
)
|
|
|
|
// ExecCommand executes a command using exec
|
|
// It returns any output/errors
|
|
func ExecCommand(name string, arg ...string) (string, error) {
|
|
var err error
|
|
cmd := exec.Command(name, arg...)
|
|
outputBytes := &bytes.Buffer{}
|
|
cmd.Stdout = outputBytes
|
|
cmd.Stderr = outputBytes
|
|
err = cmd.Start()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
err = cmd.Wait() // wait for command to finish
|
|
outstring := outputBytes.String()
|
|
return outstring, err
|
|
}
|
|
|
|
// RunCommand executes a shell command and returns the output/error
|
|
func RunCommand(input string) (string, error) {
|
|
args, err := shellquote.Split(input)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
if len(args) == 0 {
|
|
return "", errors.New("No arguments")
|
|
}
|
|
inputCmd := args[0]
|
|
|
|
return ExecCommand(inputCmd, args[1:]...)
|
|
}
|
|
|
|
// **Deprecated, don't use**
|
|
// RunBackgroundShell runs a shell command in the background
|
|
// It returns a function which will run the command and returns a string
|
|
// message result
|
|
func RunBackgroundShell(input string) (func() string, error) {
|
|
args, err := shellquote.Split(input)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if len(args) == 0 {
|
|
return nil, errors.New("No arguments")
|
|
}
|
|
inputCmd := args[0]
|
|
return func() string {
|
|
output, err := RunCommand(input)
|
|
|
|
str := output
|
|
if err != nil {
|
|
str = fmt.Sprint(inputCmd, " exited with error: ", err, ": ", output)
|
|
}
|
|
return str
|
|
}, nil
|
|
}
|
|
|
|
// ExecBackgroundCommand executes a command in the background by accepting
|
|
// a optional callback function for the command output or error if any,
|
|
// the command name and a list of arguments.
|
|
func ExecBackgroundCommand(cb func(string, error), name string, args ...string) {
|
|
go func() {
|
|
output, runErr := ExecCommand(name, args[0:]...)
|
|
if cb != nil {
|
|
wrapperFunc := func(output string, args []interface{}) {
|
|
errVal, ok := args[0].(error)
|
|
if ok {
|
|
cb(output, errVal)
|
|
} else {
|
|
cb(output, nil)
|
|
}
|
|
}
|
|
|
|
var passArgs []interface{}
|
|
passArgs = append(passArgs, runErr)
|
|
jobFunc := JobFunction{wrapperFunc, output, passArgs}
|
|
Jobs <- jobFunc
|
|
}
|
|
}()
|
|
}
|
|
|
|
// RunBackgroundCommand runs a shell command in the background by accepting
|
|
// an input for running the command and an optional callback function for
|
|
// the command output or error if any.
|
|
// It returns an error if it fails to split the input command
|
|
func RunBackgroundCommand(input string, cb func(string, error)) error {
|
|
args, err := shellquote.Split(input)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if len(args) == 0 {
|
|
return errors.New("No arguments")
|
|
}
|
|
|
|
ExecBackgroundCommand(cb, args[0], args[1:]...)
|
|
return nil
|
|
}
|
|
|
|
// RunInteractiveShell runs a shellcommand interactively
|
|
func RunInteractiveShell(input string, wait bool, getOutput bool) (string, error) {
|
|
args, err := shellquote.Split(input)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
if len(args) == 0 {
|
|
return "", errors.New("No arguments")
|
|
}
|
|
inputCmd := args[0]
|
|
|
|
// Shut down the screen because we're going to interact directly with the shell
|
|
screenb := screen.TempFini()
|
|
|
|
args = args[1:]
|
|
|
|
// Set up everything for the command
|
|
outputBytes := &bytes.Buffer{}
|
|
cmd := exec.Command(inputCmd, args...)
|
|
cmd.Stdin = os.Stdin
|
|
if getOutput {
|
|
cmd.Stdout = io.MultiWriter(os.Stdout, outputBytes)
|
|
} else {
|
|
cmd.Stdout = os.Stdout
|
|
}
|
|
cmd.Stderr = os.Stderr
|
|
|
|
// This is a trap for Ctrl-C so that it doesn't kill micro
|
|
// micro is killed if the signal is ignored only on Windows, so it is
|
|
// received
|
|
c := make(chan os.Signal, 1)
|
|
signal.Reset(os.Interrupt)
|
|
signal.Notify(c, os.Interrupt)
|
|
err = cmd.Start()
|
|
if err == nil {
|
|
err = cmd.Wait()
|
|
if wait {
|
|
// This is just so we don't return right away and let the user press enter to return
|
|
screen.TermMessage("")
|
|
}
|
|
} else {
|
|
screen.TermMessage(err)
|
|
}
|
|
|
|
output := outputBytes.String()
|
|
|
|
// Start the screen back up
|
|
screen.TempStart(screenb)
|
|
|
|
signal.Notify(util.Sigterm, os.Interrupt)
|
|
signal.Stop(c)
|
|
|
|
return output, err
|
|
}
|
|
|
|
// UserCommand runs the shell command
|
|
// The openTerm argument specifies whether a terminal should be opened (for viewing output
|
|
// or interacting with stdin)
|
|
// func UserCommand(input string, openTerm bool, waitToFinish bool) string {
|
|
// if !openTerm {
|
|
// RunBackgroundShell(input)
|
|
// return ""
|
|
// } else {
|
|
// output, _ := RunInteractiveShell(input, waitToFinish, false)
|
|
// return output
|
|
// }
|
|
// }
|