使用 <(..) 参数执行
go exec with <(..) argument
我正在尝试使用 exec
获得等效项
diff <(echo "foo") <(echo "bar")
产生:
1c1
< foo
---
> bar
但是当我尝试时:
cmd := exec.Command("diff", "<(echo foo)", "<(echo bar)")
输出为:
exit status 2
diff: <(echo foo): No such file or directory
diff: <(echo bar): No such file or directory
<(echo foo)
语法由 shell、sh
或 bash
解释。函数 exec.Command
不会根据您提供的参数调用 shell ,它直接执行一个过程。如果要调用 shell,则调用 shell:
exec.Command("bash", "-c", "diff <(echo foo) </echo bar)")
在这个例子中我使用 bash
和许多系统一样,sh
没有实现 <(cmd)
语法。尽可能使用 sh
来提高应用程序的可移植性。
FUZxxl的回答很简单,但是需要用到bash。 Bash 并不是真正的跨平台,并且作为解释器对代码注入是开放的。我将首先描述一种使用 Go 复制它的方法,然后描述 git 使用的答案。
<(echo foo) 在 bash 中做了两件事。首先,它创建一个文件描述符以传递给被调用的命令。它做的第二件事是将 /dev/fd/n 作为参数传递。为了确认,请尝试:
echo <(echo foo) <(echo bar)
Echo 忽略 fds,仅将文件描述符打印为字符串。
以下 Go 代码将执行类似的操作:
package main
import (
"io"
"log"
"os"
"os/exec"
"strings"
)
func main() {
cmd := exec.Command("diff", "/dev/fd/3", "/dev/fd/4")
foo, err := readerFile(strings.NewReader("foo\n"))
if err != nil {
log.Fatal(err)
}
defer foo.Close()
bar, err := readerFile(strings.NewReader("bar\n"))
if err != nil {
log.Fatal(err)
}
defer bar.Close()
cmd.ExtraFiles = []*os.File{foo, bar}
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err = cmd.Run()
if err != nil {
log.Fatal(err)
}
}
func readerFile(r io.Reader) (*os.File, error) {
reader, writer, err := os.Pipe()
if err != nil {
return nil, err
}
go func() {
io.Copy(writer, r)
writer.Close()
}()
return reader, nil
}
显然,这只能在类 Unix 系统上运行,因此不是很跨平台。不过它不受代码注入的影响。
GIT的方式
Git只是制作临时文件,然后进行比较。这是非常跨平台的,并且不受注入影响。只需使用 ioutil.TempFile 创建两个文件,然后 运行 exec.Command("diff", file1, file2)
.
我正在尝试使用 exec
获得等效项diff <(echo "foo") <(echo "bar")
产生:
1c1
< foo
---
> bar
但是当我尝试时:
cmd := exec.Command("diff", "<(echo foo)", "<(echo bar)")
输出为:
exit status 2
diff: <(echo foo): No such file or directory
diff: <(echo bar): No such file or directory
<(echo foo)
语法由 shell、sh
或 bash
解释。函数 exec.Command
不会根据您提供的参数调用 shell ,它直接执行一个过程。如果要调用 shell,则调用 shell:
exec.Command("bash", "-c", "diff <(echo foo) </echo bar)")
在这个例子中我使用 bash
和许多系统一样,sh
没有实现 <(cmd)
语法。尽可能使用 sh
来提高应用程序的可移植性。
FUZxxl的回答很简单,但是需要用到bash。 Bash 并不是真正的跨平台,并且作为解释器对代码注入是开放的。我将首先描述一种使用 Go 复制它的方法,然后描述 git 使用的答案。
<(echo foo) 在 bash 中做了两件事。首先,它创建一个文件描述符以传递给被调用的命令。它做的第二件事是将 /dev/fd/n 作为参数传递。为了确认,请尝试:
echo <(echo foo) <(echo bar)
Echo 忽略 fds,仅将文件描述符打印为字符串。
以下 Go 代码将执行类似的操作:
package main
import (
"io"
"log"
"os"
"os/exec"
"strings"
)
func main() {
cmd := exec.Command("diff", "/dev/fd/3", "/dev/fd/4")
foo, err := readerFile(strings.NewReader("foo\n"))
if err != nil {
log.Fatal(err)
}
defer foo.Close()
bar, err := readerFile(strings.NewReader("bar\n"))
if err != nil {
log.Fatal(err)
}
defer bar.Close()
cmd.ExtraFiles = []*os.File{foo, bar}
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err = cmd.Run()
if err != nil {
log.Fatal(err)
}
}
func readerFile(r io.Reader) (*os.File, error) {
reader, writer, err := os.Pipe()
if err != nil {
return nil, err
}
go func() {
io.Copy(writer, r)
writer.Close()
}()
return reader, nil
}
显然,这只能在类 Unix 系统上运行,因此不是很跨平台。不过它不受代码注入的影响。
GIT的方式
Git只是制作临时文件,然后进行比较。这是非常跨平台的,并且不受注入影响。只需使用 ioutil.TempFile 创建两个文件,然后 运行 exec.Command("diff", file1, file2)
.