使用<(..)参数执行go exec命令。

huangapple go评论82阅读模式
英文:

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
英文:

I'm trying to get the equivalent using exec

diff &lt;(echo &quot;foo&quot;) &lt;(echo &quot;bar&quot;)

which produces:

1c1
&lt; foo
---
&gt; bar

But when I try:

cmd := exec.Command(&quot;diff&quot;, &quot;&lt;(echo foo)&quot;, &quot;&lt;(echo bar)&quot;)

The output is:

exit status 2
diff: &lt;(echo foo): No such file or directory
diff: &lt;(echo bar): No such file or directory

答案1

得分: 4

FUZxxl的答案很简单,但需要使用bash。Bash并不是真正的跨平台,作为解释器容易受到代码注入的影响。我首先会描述一种使用Go复制这个过程的方法,然后再描述git使用的答案。

在bash中,<(echo foo)有两个作用。首先,它创建一个文件描述符,用于传递给调用的命令。其次,它将/dev/fd/n作为参数传递。你可以尝试以下命令进行确认:

echo <(echo foo) <(echo bar)

Echo会忽略文件描述符,并将其作为字符串打印出来。

以下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)

英文:

FUZxxl's answer is simple, but it requires the use of bash. Bash is not really cross platform and as an interpreter is open to code injection. I will first describe a way to replicate this with Go, then describe the answer git uses.

The <(echo foo) does two things in bash. First, it creates a file descriptor to be passed to the command being called. The second thing it does is pass /dev/fd/n as an argument. For confirmation, try:

echo &lt;(echo foo) &lt;(echo bar)

Echo ignores the fds and just prints the file descriptor as a string.

The following Go code will do something similar:

package main

import (
	&quot;io&quot;
	&quot;log&quot;
	&quot;os&quot;
	&quot;os/exec&quot;
	&quot;strings&quot;
)

func main() {
	cmd := exec.Command(&quot;diff&quot;, &quot;/dev/fd/3&quot;, &quot;/dev/fd/4&quot;)

	foo, err := readerFile(strings.NewReader(&quot;foo\n&quot;))
	if err != nil {
		log.Fatal(err)
	}
	defer foo.Close()

	bar, err := readerFile(strings.NewReader(&quot;bar\n&quot;))
	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
}

Obviously, this will only work on Unix like systems and is therefore not very cross platform. It is immune to code injection though.


GIT's way

Git just makes temporary files and then compares them. This is extremely cross platform and immune to injections. Just use ioutil.TempFile to create both the files and then run exec.Command(&quot;diff&quot;, file1, file2).

答案2

得分: 3

&lt;(echo foo) 语法是由 shell(如 shbash)解释的。exec.Command 函数不会在提供的参数上调用 shell,它直接执行一个进程。如果你想要调用 shell,那么就调用 shell:

exec.Command("bash", "-c", "diff <(echo foo) <(echo bar)")

在这个例子中,我使用 bash,因为在许多系统上,sh 不支持 &lt;(cmd) 语法。尽可能使用 sh 来增加应用程序的可移植性。

英文:

The &lt;(echo foo) syntax is interpreted by the shell, sh or bash. The function exec.Command does not invoke the shell on the arguments you provide, it directly executes a process. If you want to invoke the shell, then invoke the shell:

exec.Command(&quot;bash&quot;, &quot;-c&quot;, &quot;diff &lt;(echo foo) &lt;/echo bar)&quot;)

In this example I use bash as on many systems, sh does not implement the &lt;(cmd) syntax. Try to use sh whenever possible to increase the portability of your applications.

huangapple
  • 本文由 发表于 2015年3月13日 08:46:56
  • 转载请务必保留本文链接:https://go.coder-hub.com/29022974.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定