diff options
author | Junegunn Choi <junegunn.c@gmail.com> | 2024-04-27 18:36:37 +0900 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-04-27 18:36:37 +0900 |
commit | a4391aeedd4fec1865d2d646711f58d04058531b (patch) | |
tree | 73a6862010c323f380a3105f929b41a39c7a3753 /src/util/util_unix.go | |
parent | b86a967ee217f4c820249701218a17eaad2737ae (diff) |
Add --with-shell for shelling out with different command and flags (#3746)
Close #3732
Diffstat (limited to 'src/util/util_unix.go')
-rw-r--r-- | src/util/util_unix.go | 56 |
1 files changed, 48 insertions, 8 deletions
diff --git a/src/util/util_unix.go b/src/util/util_unix.go index 2991fd2c..4410a9bf 100644 --- a/src/util/util_unix.go +++ b/src/util/util_unix.go @@ -3,31 +3,71 @@ package util import ( + "fmt" "os" "os/exec" + "strings" "syscall" "golang.org/x/sys/unix" ) -// ExecCommand executes the given command with $SHELL -func ExecCommand(command string, setpgid bool) *exec.Cmd { +type Executor struct { + shell string + args []string + escaper *strings.Replacer +} + +func NewExecutor(withShell string) *Executor { shell := os.Getenv("SHELL") - if len(shell) == 0 { - shell = "sh" + args := strings.Fields(withShell) + if len(args) > 0 { + shell = args[0] + args = args[1:] + } else { + if len(shell) == 0 { + shell = "sh" + } + args = []string{"-c"} } - return ExecCommandWith(shell, command, setpgid) + + var escaper *strings.Replacer + tokens := strings.Split(shell, "/") + if tokens[len(tokens)-1] == "fish" { + // https://fishshell.com/docs/current/language.html#quotes + // > The only meaningful escape sequences in single quotes are \', which + // > escapes a single quote and \\, which escapes the backslash symbol. + escaper = strings.NewReplacer("\\", "\\\\", "'", "\\'") + } else { + escaper = strings.NewReplacer("'", "'\\''") + } + return &Executor{shell, args, escaper} } -// ExecCommandWith executes the given command with the specified shell -func ExecCommandWith(shell string, command string, setpgid bool) *exec.Cmd { - cmd := exec.Command(shell, "-c", command) +// ExecCommand executes the given command with $SHELL +func (x *Executor) ExecCommand(command string, setpgid bool) *exec.Cmd { + cmd := exec.Command(x.shell, append(x.args, command)...) if setpgid { cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true} } return cmd } +func (x *Executor) QuoteEntry(entry string) string { + return "'" + x.escaper.Replace(entry) + "'" +} + +func (x *Executor) Become(stdin *os.File, environ []string, command string) { + shellPath, err := exec.LookPath(x.shell) + if err != nil { + fmt.Fprintf(os.Stderr, "fzf (become): %s\n", err.Error()) + Exit(127) + } + args := append([]string{shellPath}, append(x.args, command)...) + SetStdin(stdin) + syscall.Exec(shellPath, args, environ) +} + // KillCommand kills the process for the given command func KillCommand(cmd *exec.Cmd) error { return syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL) |