问题描述
我正在尝试从go调用git日志,并将输出重定向到给定的文件.
I am trying to invoke a git log from go and redirect the output to a given file.
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中执行命令没有问题.
Executing the command in bash directly poses no problem.
推荐答案
Go的cmd.Run()
的行为类似于启动新程序的C fork()
和exec()
进程.该不会隐式调用shell-从安全角度来看,这是一件非常好的事情;不必要的shell调用通常会导致命令注入漏洞.
Go's cmd.Run()
acts similarly to the C fork()
and exec()
process for starting a new program. This doesn't implicitly invoke a shell -- which is, from a security perspective, an extremely good thing; unnecessary shell invocation often leads to command injection vulnerabilities.
如果您想要外壳添加的功能(此处为重定向和复合命令语法),但又想避免安全风险,请通过代码将数据带外传递:
If you want capabilities that a shell can add -- here, redirection and compound command syntax -- but want to avoid the security risks, pass your data out-of-band from code:
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脚本:
The above is equivalent to the following shell script:
#!/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=
内部删除了文字引号.这是因为,当您在外壳程序中运行命令时,外壳程序会将这些引号视为语法 -一条指令,不得在格式字符串内的空格上拆分.在这里,没有外壳程序将该字符串解析为代码.如果我们将引号保留下来,它们将成为格式字符串的文字部分.
Note that I removed the literal quotes from inside --pretty=
. That's because when you run the command in a shell, those quotes are treated as syntax by the shell -- an instruction not to split on the spaces within the format string. Here, there is no shell parsing that string as code; if we left the quotes in, they would become a literal part of your format string.
这篇关于exec git命令拒绝重定向到Go中的文件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!