我正在尝试从go调用git日志,并将输出重定向到给定的文件。
cmdArgs = []string{"log", "--numstat", "--reverse", fmt.Sprintf("%s..HEAD", "89c98f5ec48c8ac383ea9e27d792c3dc77fa6240"), `--pretty="format:=%P %H %an %ae %ad %at %s %b"`} cmdArgs = append(cmdArgs, ">> "+workingDir+"/logs/"+repoName+".log && cat "+workingDir+"/logs/"+repoName+".log") cmd := exec.Command("git", cmdArgs...) cmd.Dir = workingDir + repoName var out bytes.Buffer var stderr bytes.Buffer cmd.Stdout = &out cmd.Stderr = &stderr err := cmd.Run() if err != nil { fmt.Println("git", strings.Join(cmdArgs, " "), "in", workingDir+repoName) fmt.Println(fmt.Sprint(err) + ": " + stderr.String()) panic(err) }
失败于
git log --numstat --reverse --pretty="format:=%P %H %an %ae %ad %at %s %b" 89c98f5ec48c8ac383ea9e27d792c3dc77fa6240..HEAD > /someplace/xx-tmp.log && cat /someplace/xx-tmp.log in /someplace exit status 128: fatal: ambiguous argument ' > /someplace/xx-tmp.log && cat /someplace/xx-tmp.log: unknown revision or path not in the working tree. Use '--' to separate paths from revisions, like this: 'git <command> [<revision>...] -- [<file>...]'
直接在bash中执行命令没有问题。
Go的cmd.Run()行为类似于C fork()和exec()启动新程序的过程。这 并不是 隐式调用Shell,从安全性的角度来看,这是一件非常好的事情。不必要的外壳程序调用通常会导致命令注入漏洞。
cmd.Run()
fork()
exec()
如果您 想要 外壳程序可以添加的功能(此处为重定向和复合命令语法),但又想避免安全风险,请从代码中将数据带外传递:
cmdArgs = []string{ "-c", // tells interpreter that script is next argument `outfile=$1; shift; "$@" >"$outfile" && cat "$outfile"`, // script to execute "_", // this is $0 workingDir+"/logs/"+repoName+".log", // $1, becomes outfile "git", "log", "--numstat", "--reverse", // remaining args are in "$@" fmt.Sprintf("%s..HEAD", "89c98f5ec48c8ac383ea9e27d792c3dc77fa6240"), "--pretty=format:=%P %H %an %ae %ad %at %s %b" } cmd := exec.Command("sh", cmdArgs...)
上面的内容等效于以下shell脚本:
#!/bin/sh # ^^- not /bin/bash; this only guarantees support for POSIX syntax outfile=$1 # assign first positional argument to variable "$outfile" shift # rename $2 to $1, $3 to $2, etc if "$@" >"$outfile"; then # run remaining arguments as a single command, stdout to outfile cat "$outfile" # if that succeeded, then cat our "$outfile" to stdout fi
请注意,我从内部删除了文字引号--pretty=。这是因为当你在shell中运行命令,这些报价作为语法处理 的外壳 -不要在格式字符串中的空间分割的指令。这里,没有外壳程序将该字符串解析为代码。如果我们将引号保留下来,它们将成为格式字符串的文字部分。
--pretty=