--tmux: Export bash functions

Fix #4001
This commit is contained in:
Junegunn Choi 2024-09-22 00:31:11 +09:00
parent 53bcdc4294
commit 4161403a1d
No known key found for this signature in database
GPG Key ID: 254BC280FEF9C627
5 changed files with 50 additions and 23 deletions

View File

@ -9,6 +9,7 @@ import (
"os/exec" "os/exec"
"os/signal" "os/signal"
"path/filepath" "path/filepath"
"regexp"
"strings" "strings"
"time" "time"
@ -32,7 +33,7 @@ func fifo(name string) (string, error) {
return output, nil return output, nil
} }
func runProxy(commandPrefix string, cmdBuilder func(temp string) *exec.Cmd, opts *Options, withExports bool) (int, error) { func runProxy(commandPrefix string, cmdBuilder func(temp string, needBash bool) (*exec.Cmd, error), opts *Options, withExports bool) (int, error) {
output, err := fifo("proxy-output") output, err := fifo("proxy-output")
if err != nil { if err != nil {
return ExitError, err return ExitError, err
@ -92,17 +93,28 @@ func runProxy(commandPrefix string, cmdBuilder func(temp string) *exec.Cmd, opts
// To ensure that the options are processed by a POSIX-compliant shell, // To ensure that the options are processed by a POSIX-compliant shell,
// we need to write the command to a temporary file and execute it with sh. // we need to write the command to a temporary file and execute it with sh.
var exports []string var exports []string
needBash := false
if withExports { if withExports {
exports = os.Environ() validIdentifier := regexp.MustCompile(`^[a-zA-Z_][a-zA-Z0-9_]*$`)
for idx, pairStr := range exports { for _, pairStr := range os.Environ() {
pair := strings.SplitN(pairStr, "=", 2) pair := strings.SplitN(pairStr, "=", 2)
exports[idx] = fmt.Sprintf("export %s=%s", pair[0], escapeSingleQuote(pair[1])) if validIdentifier.MatchString(pair[0]) {
exports = append(exports, fmt.Sprintf("export %s=%s", pair[0], escapeSingleQuote(pair[1])))
} else if strings.HasPrefix(pair[0], "BASH_FUNC_") && strings.HasSuffix(pair[0], "%%") {
name := pair[0][10 : len(pair[0])-2]
exports = append(exports, name+pair[1])
exports = append(exports, "export -f "+name)
needBash = true
}
} }
} }
temp := WriteTemporaryFile(append(exports, command), "\n") temp := WriteTemporaryFile(append(exports, command), "\n")
defer os.Remove(temp) defer os.Remove(temp)
cmd := cmdBuilder(temp) cmd, err := cmdBuilder(temp, needBash)
if err != nil {
return ExitError, err
}
cmd.Stderr = os.Stderr cmd.Stderr = os.Stderr
intChan := make(chan os.Signal, 1) intChan := make(chan os.Signal, 1)
defer close(intChan) defer close(intChan)

View File

@ -9,7 +9,10 @@ import (
"golang.org/x/sys/unix" "golang.org/x/sys/unix"
) )
func sh() (string, error) { func sh(bash bool) (string, error) {
if bash {
return "bash", nil
}
return "sh", nil return "sh", nil
} }

View File

@ -13,12 +13,16 @@ import (
var shPath atomic.Value var shPath atomic.Value
func sh() (string, error) { func sh(bash bool) (string, error) {
if cached := shPath.Load(); cached != nil { if cached := shPath.Load(); cached != nil {
return cached.(string), nil return cached.(string), nil
} }
cmd := exec.Command("cygpath", "-w", "/usr/bin/sh") name := "sh"
if bash {
name = "bash"
}
cmd := exec.Command("cygpath", "-w", "/usr/bin/"+name)
bytes, err := cmd.Output() bytes, err := cmd.Output()
if err != nil { if err != nil {
return "", err return "", err
@ -31,7 +35,7 @@ func sh() (string, error) {
func mkfifo(path string, mode uint32) (string, error) { func mkfifo(path string, mode uint32) (string, error) {
m := strconv.FormatUint(uint64(mode), 8) m := strconv.FormatUint(uint64(mode), 8)
sh, err := sh() sh, err := sh(false)
if err != nil { if err != nil {
return path, err return path, err
} }
@ -43,7 +47,7 @@ func mkfifo(path string, mode uint32) (string, error) {
} }
func withOutputPipe(output string, task func(io.ReadCloser)) error { func withOutputPipe(output string, task func(io.ReadCloser)) error {
sh, err := sh() sh, err := sh(false)
if err != nil { if err != nil {
return err return err
} }
@ -62,7 +66,7 @@ func withOutputPipe(output string, task func(io.ReadCloser)) error {
} }
func withInputPipe(input string, task func(io.WriteCloser)) error { func withInputPipe(input string, task func(io.WriteCloser)) error {
sh, err := sh() sh, err := sh(false)
if err != nil { if err != nil {
return err return err
} }

View File

@ -49,9 +49,12 @@ func runTmux(args []string, opts *Options) (int, error) {
tmuxArgs = append(tmuxArgs, "-w"+opts.Tmux.width.String()) tmuxArgs = append(tmuxArgs, "-w"+opts.Tmux.width.String())
tmuxArgs = append(tmuxArgs, "-h"+opts.Tmux.height.String()) tmuxArgs = append(tmuxArgs, "-h"+opts.Tmux.height.String())
return runProxy(argStr, func(temp string) *exec.Cmd { return runProxy(argStr, func(temp string, needBash bool) (*exec.Cmd, error) {
sh, _ := sh() sh, err := sh(needBash)
if err != nil {
return nil, err
}
tmuxArgs = append(tmuxArgs, sh, temp) tmuxArgs = append(tmuxArgs, sh, temp)
return exec.Command("tmux", tmuxArgs...) return exec.Command("tmux", tmuxArgs...), nil
}, opts, true) }, opts, true)
} }

View File

@ -44,11 +44,6 @@ func needWinpty(opts *Options) bool {
} }
func runWinpty(args []string, opts *Options) (int, error) { func runWinpty(args []string, opts *Options) (int, error) {
sh, err := sh()
if err != nil {
return ExitError, err
}
argStr := escapeSingleQuote(args[0]) argStr := escapeSingleQuote(args[0])
for _, arg := range args[1:] { for _, arg := range args[1:] {
argStr += " " + escapeSingleQuote(arg) argStr += " " + escapeSingleQuote(arg)
@ -56,20 +51,30 @@ func runWinpty(args []string, opts *Options) (int, error) {
argStr += ` --no-winpty` argStr += ` --no-winpty`
if isMintty345() { if isMintty345() {
return runProxy(argStr, func(temp string) *exec.Cmd { return runProxy(argStr, func(temp string, needBash bool) (*exec.Cmd, error) {
sh, err := sh(needBash)
if err != nil {
return nil, err
}
cmd := exec.Command(sh, temp) cmd := exec.Command(sh, temp)
cmd.Env = append(os.Environ(), "MSYS=enable_pcon") cmd.Env = append(os.Environ(), "MSYS=enable_pcon")
cmd.Stdin = os.Stdin cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr cmd.Stderr = os.Stderr
return cmd return cmd, nil
}, opts, false) }, opts, false)
} }
return runProxy(argStr, func(temp string) *exec.Cmd { return runProxy(argStr, func(temp string, needBash bool) (*exec.Cmd, error) {
sh, err := sh(needBash)
if err != nil {
return nil, err
}
cmd := exec.Command(sh, "-c", fmt.Sprintf(`winpty < /dev/tty > /dev/tty -- sh %q`, temp)) cmd := exec.Command(sh, "-c", fmt.Sprintf(`winpty < /dev/tty > /dev/tty -- sh %q`, temp))
cmd.Stdout = os.Stdout cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr cmd.Stderr = os.Stderr
return cmd return cmd, nil
}, opts, false) }, opts, false)
} }