Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Running a command from a string with parameters in go

Tags:

go

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.

like image 308
Rafa de Castro Avatar asked Nov 14 '18 08:11

Rafa de Castro


People also ask

How do you pass command-line arguments in Golang?

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.

What is args Golang?

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.

What is exec command?

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).


2 Answers

$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.

like image 114
Flimzy Avatar answered Nov 15 '22 07:11

Flimzy


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.

like image 29
noamt Avatar answered Nov 15 '22 09:11

noamt