我正在尝试用go运行一个命令。这个命令是字符串形式的。
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)
}
}
输出为:
2018/11/14 09:41:22 Running echo $HOME
([]string) (len=1 cap=1) {
(string) (len=5) "$HOME"
}
$HOME
我想要的是:
2018/11/14 09:41:22 Running echo $HOME
([]string) (len=1 cap=1) {
(string) (len=5) "$HOME"
}
/home/whatever
看起来go正在清理这个字符串,所以$HOME
没有被扩展,有没有办法运行这个字符串,就像它被输入shell一样?
这是最重要的部分,理想情况下,我希望在当前shell中从字符串转换为类型。
编辑:下面的例子解决了最简单的场景,但没有涉及"运行字符串,就像它被输入到shell中一样"部分。
如果我切换到expandenv
:
commandToRun := os.ExpandEnv(`echo "$HOME"`)
我得到:
2018/11/14 11:45:44 Running echo "/Users/rafael"
([]string) (len=1 cap=1) {
(string) (len=15) "\"/home/whatever\""
}
"/home/whatever"
我在壳里得到的是:
$ > echo "$HOME"
/home/whatever
没有引号。
这和我想要的差不多,但不完全一样。
3条答案
按热度按时间1sbrub3j1#
$HOME
(以及所有其他env变量)是由shell展开的,因为您没有执行shell,所以它们不会被展开。你需要直接在go中查找env变量,如下所示:
或者这个:
请注意,如果
$HOME
扩展为包含空格的字符串,则最后一种方法将不起作用,因此os.Getenv
方法通常更安全/更适合此用例。4si2a6ki2#
在执行命令之前,可以使用
os.ExpandEnv
主动展开字符串中的所有env变量:来自文档:
ExpandEnv根据当前环境变量的值替换字符串中的${var}或$var。对未定义变量的引用被替换为空字符串。
t1rydlwq3#
如果要获得
$ echo $HOME
的输出,最少需要以下代码不需要更多了。
os.ExpandEnv("echo $HOME")
,则首先会展开$HOME
var,然后它会提供类似echo /home/<user>
的字符串command := exec.Command("echo", os.Getenv("HOME"))
,则它将被解析为command := exec.Command("echo", "/home/<user>")
,最后将得到输出/home/<user>
commandToRun := os.ExpandEnv("echo $HOME") command := exec.Command(strings.Fields(commandToRun)...)
那么它将像先前的情况一样被处理。
所以更好的方法是只使用
fmt.Println(os.Getenv("HOME"))
。