allow using special characters in external commands (#1652) (#1868)

on Windows, when using cmd.exe or a bat file as external command.
This commit is contained in:
Alessandro Ros 2023-05-27 18:20:06 +02:00 committed by GitHub
parent 27a2418663
commit a0b973963d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 54 additions and 30 deletions

View File

@ -33,6 +33,8 @@ func NewCmd(
env Environment,
onExit func(int),
) *Cmd {
// replace variables in both Linux and Windows, in order to allow using the
// same commands on both of them.
for key, val := range env {
cmdstr = strings.ReplaceAll(cmdstr, "$"+key, val)
}
@ -62,28 +64,30 @@ func (e *Cmd) run() {
defer e.pool.wg.Done()
for {
ok := func() bool {
c, ok := e.runInner()
if !ok {
return false
}
e.onExit(c)
if !e.restart {
<-e.terminate
return false
}
select {
case <-time.After(restartPause):
return true
case <-e.terminate:
return false
}
}()
ok := e.runInner()
if !ok {
break
}
}
}
func (e *Cmd) runInner() bool {
c, ok := e.runOSSpecific()
if !ok {
return false
}
e.onExit(c)
if !e.restart {
<-e.terminate
return false
}
select {
case <-time.After(restartPause):
return true
case <-e.terminate:
return false
}
}

View File

@ -11,13 +11,13 @@ import (
"github.com/kballard/go-shellquote"
)
func (e *Cmd) runInner() (int, bool) {
cmdparts, err := shellquote.Split(e.cmdstr)
func (e *Cmd) runOSSpecific() (int, bool) {
cmdParts, err := shellquote.Split(e.cmdstr)
if err != nil {
return 0, true
}
cmd := exec.Command(cmdparts[0], cmdparts[1:]...)
cmd := exec.Command(cmdParts[0], cmdParts[1:]...)
cmd.Env = append([]string(nil), os.Environ()...)
for key, val := range e.env {

View File

@ -6,17 +6,37 @@ package externalcmd
import (
"os"
"os/exec"
"strings"
"syscall"
"github.com/kballard/go-shellquote"
)
func (e *Cmd) runInner() (int, bool) {
cmdparts, err := shellquote.Split(e.cmdstr)
if err != nil {
return 0, true
}
func (e *Cmd) runOSSpecific() (int, bool) {
var cmd *exec.Cmd
cmd := exec.Command(cmdparts[0], cmdparts[1:]...)
// from Golang documentation:
// On Windows, processes receive the whole command line as a single string and do their own parsing.
// Command combines and quotes Args into a command line string with an algorithm compatible with
// applications using CommandLineToArgvW (which is the most common way). Notable exceptions are
// msiexec.exe and cmd.exe (and thus, all batch files), which have a different unquoting algorithm.
// In these or other similar cases, you can do the quoting yourself and provide the full command
// line in SysProcAttr.CmdLine, leaving Args empty.
if strings.HasPrefix(e.cmdstr, "cmd ") || strings.HasPrefix(e.cmdstr, "cmd.exe ") {
args := strings.TrimPrefix(strings.TrimPrefix(e.cmdstr, "cmd "), "cmd.exe ")
cmd = exec.Command("cmd.exe")
cmd.SysProcAttr = &syscall.SysProcAttr{
CmdLine: args,
}
} else {
cmdParts, err := shellquote.Split(e.cmdstr)
if err != nil {
return 0, true
}
cmd = exec.Command(cmdParts[0], cmdParts[1:]...)
}
cmd.Env = append([]string(nil), os.Environ()...)
for key, val := range e.env {
@ -26,7 +46,7 @@ func (e *Cmd) runInner() (int, bool) {
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err = cmd.Start()
err := cmd.Start()
if err != nil {
return 0, true
}