lifecycle/launch/bash.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)
}