I am in a situation where I am trying to execute a shell command, but have its arguments be interpreted as environment variables properly.
For example, when I type the following into the terminal
ls $GOPATH
Bash interprets and expands the variable $GOPATH
, and lists the contents of the $GOPATH
directory. I am trying to do a similar thing with Golang's programmatic shell execution.
I have the following code.
package main
import (
"bytes"
"fmt"
"log"
"os"
"os/exec"
)
func main() {
cmd := exec.Command("echo", "$TESTVAR")
cmd.Env = append(os.Environ(),
"TESTVAR=this_is_a_test",
)
var outBuff bytes.Buffer
var errBuff bytes.Buffer
cmd.Stdout = &outBuff
cmd.Stderr = &errBuff
if err := cmd.Run(); err != nil {
log.Fatal(err)
}
fmt.Println(outBuff.String()) // empty
fmt.Println(errBuff.String()) // empty
}
This program outputs
$ go run test.go
$TESTVAR
Does anyone have any idea how to make the exec library interpret $TESTVAR as an environment variable as opposed to a string literal? Thanks in advance!
Replace
cmd := exec.Command("echo", "$TESTVAR")
with
cmd := exec.Command("sh", "-c", "echo $TESTVAR")
Bash and other shells interprets and expands variables, but the application is not executing Bash.
The application is executing the echo command. The echo command, like most other commands, does not expand environment variables in its arguments.
You can either run Bash as shown in another answer or expand environment variables on your own. Here's how to use os.Expand function to do this:
func newCommandEnv(env []string, cmd string, args ...string) *exec.Cmd {
m := map[string]string{}
for _, e := range env {
if i := strings.Index(e, "="); i >= 0 {
m[e[:i]] = e[i+1:]
}
}
fn := func(placeholder string) string {
return m[placeholder]
}
for i, a := range args {
args[i] = os.Expand(a, fn)
}
fmt.Println(args)
c := exec.Command(cmd, args...)
c.Env = env
return c
}