62 lines
2.1 KiB
Go
62 lines
2.1 KiB
Go
package launch
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
var (
|
|
bashCommandWithScript = `exec bash -c "$@"` // for processes w/o arguments
|
|
)
|
|
|
|
type BashShell struct {
|
|
Exec ExecFunc
|
|
}
|
|
|
|
// Launch launches the given ShellProcess with Bash
|
|
//
|
|
// It shall execute a Bash command that sources profile scripts and then executes the process in a nested Bash command
|
|
// When ShellProcess.Script is true nested Bash script shall be proc.Command with proc.Args provided as argument to Bash
|
|
// When ShellProcess.Script is false a Bash command shall be contructed from proc.Command and proc.Args
|
|
func (b *BashShell) Launch(proc ShellProcess) error {
|
|
launcher := ""
|
|
for _, profile := range proc.Profiles {
|
|
launcher += fmt.Sprintf("source \"%s\"\n", profile)
|
|
}
|
|
launcher += fmt.Sprintf("cd \"%s\"\n", proc.WorkingDirectory)
|
|
var bashCommand string
|
|
if proc.Script {
|
|
bashCommand = bashCommandWithScript
|
|
} else {
|
|
bashCommand = bashCommandWithTokens(len(proc.Args) + 1)
|
|
}
|
|
launcher += bashCommand
|
|
if err := b.Exec("/bin/bash", append([]string{
|
|
"bash", "-c",
|
|
launcher, proc.Caller, proc.Command,
|
|
}, proc.Args...), proc.Env); err != nil {
|
|
return errors.Wrap(err, "bash exec")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// bashCommandWithTokens returns a bash script that should be executed with nTokens number of bash arguments
|
|
//
|
|
// Each argument to bash is evaluated by the shell before becoming a token in the resulting script
|
|
// Example:
|
|
// Given nTokens=2 the returned script will contain `"$(eval echo \"$0\")" "$(eval echo \"$1\")"`
|
|
// and should be evaluated with `bash -c '"$(eval echo \"$0\")" "$(eval echo \"$1\")"' <command> <arg>'
|
|
// Token evaluation example:
|
|
// "$(eval echo \"$0\"`)" // given $0='$FOO' and $FOO='arg with spaces" && quotes'
|
|
// -> "$(eval echo \"'$FOO'\")"
|
|
// -> "$(echo \"'arg with spaces" && quotes'\")"
|
|
// -> "arg with spaces\" && quotes" // this is an evaluated and properly quoted token
|
|
func bashCommandWithTokens(nTokens int) string {
|
|
commandScript := `"$(eval echo \"$0\")"`
|
|
for i := 1; i < nTokens; i++ {
|
|
commandScript += fmt.Sprintf(` "$(eval echo \"${%d}\")"`, i)
|
|
}
|
|
return fmt.Sprintf(`exec bash -c '%s' "${@:1}"`, commandScript)
|
|
}
|