在程序化Shell命令执行中嵌入环境变量

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
}