I am trying to run a command with go. The command is in a string.
package main
import (
"log"
"os"
"os/exec"
"strings"
"github.com/davecgh/go-spew/spew"
)
func main() {
commandToRun := `echo $HOME`
log.Printf("Running %s\n", commandToRun)
args := strings.Fields(commandToRun)
spew.Dump(args[1:len(args)])
command := exec.Command(args[0], args[1:len(args)]...)
command.Stdout = os.Stdout
command.Stdin = os.Stdin
command.Stderr = os.Stderr
err := command.Run()
if err != nil {
log.Printf("Command finished with error: %v", err)
}
}
The output is:
2018/11/14 09:41:22 Running echo $HOME
([]string) (len=1 cap=1) {
(string) (len=5) "$HOME"
}
$HOME
What I'd like to have is:
2018/11/14 09:41:22 Running echo $HOME
([]string) (len=1 cap=1) {
(string) (len=5) "$HOME"
}
/home/whatever
Looks like go is sanitizing the string somehow. So the $HOME
is not expanded. Is there any way of running the string exactly as if it was typed into the shell?
This is the important part. Ideally I'd like to turn from string to type in the current shell.
EDIT: The example below solve the simplest scenario but doesn't cover the "running the string exactly as if it was typed into the shell" part.
If I switch to expandenv
:
commandToRun := os.ExpandEnv(`echo "$HOME"`)
I get:
2018/11/14 11:45:44 Running echo "/Users/rafael"
([]string) (len=1 cap=1) {
(string) (len=15) "\"/home/whatever\""
}
"/home/whatever"
What I'd get in the shell is:
$ > echo "$HOME"
/home/whatever
without the quotes.
This is close to what I want but not exactly it.
To access all command-line arguments in their raw format, we need to use Args variables imported from the os package . This type of variable is a string ( [] ) slice. Args is an argument that starts with the name of the program in the command-line. The first value in the Args slice is the name of our program, while os.
In Golang, we have a package called as os package that contains an array called as “Args”. Args is an array of string that contains all the command line arguments passed. The first argument will be always the program name as shown below.
The exec command is a powerful tool for manipulating file-descriptors (FD), creating output and error logging within scripts with a minimal change. In Linux, by default, file descriptor 0 is stdin (the standard input), 1 is stdout (the standard output), and 2 is stderr (the standard error).
$HOME
(and all other env variables) are expanded by the shell. You're not executing a shell, so they don't get expanded.
You need to look up the env variable directly in go, with something like:
command := exec.Command("echo", os.Getenv("HOME"))
or this:
commandToRun := os.ExpandEnv("echo $HOME")
command := exec.Command(strings.Fields(commandToRun)...)
Note that this last approach won't work if $HOME
expands to a string containing whitespace, so the os.Getenv
method is generally safer/preferred for this use case.
Before executing the command, you can actively expand all env vars in the string using os.ExpandEnv
:
os.ExpandEnv("echo $HOME")
From the docs:
ExpandEnv replaces ${var} or $var in the string according to the values of the current environment variables. References to undefined variables are replaced by the empty string.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With