无法将 shell 命令的输出写入 Go 中的文件
Failure to write output of shell command to file in Go
我编写了以下函数来执行 snt2cooc
命令(运行ning [=24= 的预处理步骤之一)。出于我们的目的,我认为我们可以考虑 snt2cooc
脚本为黑框):
func SNTToCOOC(srcVocab, tgtVocab, sntPath, outpath string) error {
// open the out file for writing
outfile, err := os.Create(outpath)
if err != nil {
return err
}
defer outfile.Close()
cmdStr := "snt2cooc"
args := []string{srcVocab, tgtVocab, sntPath}
cmd := exec.Command(cmdStr, args...)
cmd.Stdout = outfile
if err = cmd.Run(); err != nil {
return err
}
cmd.Wait()
return err
}
当运行ning时,函数执行没有错误,但输出文件为空。同样的代码适用于其他类似的命令,但不适用于这个特定的 snt2cooc
命令,我注意到当我 运行 直接在 shell 中使用这个命令时:
snt2cooc file1.vcb file2.vcb file3.snt
我得到以下输出:
END.
0 2
0 3
0 4
0 5
0 6
(t运行为简洁起见)
如果我直接从 shell:
将命令的输出发送到文件
snt2cooc file1.vcb file2.vcb file3.snt > out.txt
out.txt
的内容符合预期:
0 2
0 3
0 4
0 5
0 6
注意在第一种情况下,行 END.
首先输出到标准输出,然后才将命令的实际输出发送到标准输出。因此,我认为存在竞争条件,Go 代码在命令的最终输出写入文件之前完成执行。这尽管调用 cmd.Wait()
。我不太确定 snt2cooc
命令在内部到底做了什么。有人可以提供有关如何解决此问题的提示吗?
编辑 1:
看起来像下面的代码,包括 500 毫秒的睡眠,始终将输出写入 snt2cooc
命令的文件:
cmdStr := "snt2cooc"
args := []string{srcVocab, tgtVocab, sntPath}
cmd := exec.Command(cmdStr, args...)
stdout, err := cmd.StdoutPipe()
time.Sleep(500 * time.Millisecond)
if err != nil {
return err
}
err = cmd.Start()
if err != nil {
return err
}
out := bufio.NewScanner(stdout)
for out.Scan() {
outfile.Write(out.Bytes())
outfile.WriteString("\n")
}
if err := out.Err(); err != nil {
return err
}
这向我证明存在一些竞争条件,Go 程序在所有输出写入文件之前退出。我为这个问题添加了赏金,希望有人可以 1) 解释为什么会发生这种情况,以及 2) 提供一种非 hacky 方式(即 500 毫秒睡眠)来修复它。
问题不在于 SNTtoCooc
的内部,而是您如何使用 cmd.Stdout
:
写入文件本身
func anyWrite(args []string, outpath string) error {
outfile, err := os.Create(outpath)
if err != nil {
return err
}
defer outfile.Close()
// I use simple "echo" here
cmd := exec.Command("echo", args...)
stdout, err := cmd.Output()
if err != nil {
return err
}
// Use this instead of cmd.Stdout seems to solve the problem
outfile.Write(stdout)
return nil
}
func main() {
args := []string{"Line 1", "Line 2", "Line 3"}
if err := anyWrite(args, "./outfile.txt"); err != nil {
panic(err)
}
}
根据 os/exec
中的 comments
Stdout and Stderr specify the process's standard output and error. If
either is nil, Run connects the corresponding file descriptor to the
null device (os.DevNull).
首先,清理你的代码。
cmd.Stderr = os.DevNull
,所以你忽略stderr。 Stdout 和 Stderr 指定进程的标准输出和错误。如果任一个为 nil,运行 将相应的文件描述符连接到空设备 (os.DevNull)。
cmd.Wait()
returnserror,
你无视吧。 func (c *Cmd) Wait() error
.
Wait
等待命令退出。它必须由 Start.
启动 你使用 Run,
而不是 Start.
当您 运行 这段代码时,您会得到什么输出?
failure.go
:
package main
import (
"fmt"
"os"
"os/exec"
)
func main() {
err := SNTToCOOC("file1.vcb", "file2.vcb", "file3.snt", "out.txt")
if err != nil {
fmt.Println(err)
}
}
func SNTToCOOC(srcVocab, tgtVocab, sntPath, outpath string) error {
outfile, err := os.Create(outpath)
if err != nil {
return err
}
defer outfile.Close()
cmdStr := "snt2cooc"
args := []string{srcVocab, tgtVocab, sntPath}
cmd := exec.Command(cmdStr, args...)
cmd.Stdout = outfile
cmd.Stderr = os.Stderr
err = cmd.Run()
if err != nil {
return err
}
return err
}
运行:
$ rm -f out.txt && go run failure.go && cat out.txt
此外,当您 运行 这段代码用 cmd.Stdout = os.Stdout
代替 cmd.Stdout = outfile
时,您会得到什么输出。
我编写了以下函数来执行 snt2cooc
命令(运行ning [=24= 的预处理步骤之一)。出于我们的目的,我认为我们可以考虑 snt2cooc
脚本为黑框):
func SNTToCOOC(srcVocab, tgtVocab, sntPath, outpath string) error {
// open the out file for writing
outfile, err := os.Create(outpath)
if err != nil {
return err
}
defer outfile.Close()
cmdStr := "snt2cooc"
args := []string{srcVocab, tgtVocab, sntPath}
cmd := exec.Command(cmdStr, args...)
cmd.Stdout = outfile
if err = cmd.Run(); err != nil {
return err
}
cmd.Wait()
return err
}
当运行ning时,函数执行没有错误,但输出文件为空。同样的代码适用于其他类似的命令,但不适用于这个特定的 snt2cooc
命令,我注意到当我 运行 直接在 shell 中使用这个命令时:
snt2cooc file1.vcb file2.vcb file3.snt
我得到以下输出:
END.
0 2
0 3
0 4
0 5
0 6
(t运行为简洁起见)
如果我直接从 shell:
将命令的输出发送到文件snt2cooc file1.vcb file2.vcb file3.snt > out.txt
out.txt
的内容符合预期:
0 2
0 3
0 4
0 5
0 6
注意在第一种情况下,行 END.
首先输出到标准输出,然后才将命令的实际输出发送到标准输出。因此,我认为存在竞争条件,Go 代码在命令的最终输出写入文件之前完成执行。这尽管调用 cmd.Wait()
。我不太确定 snt2cooc
命令在内部到底做了什么。有人可以提供有关如何解决此问题的提示吗?
编辑 1:
看起来像下面的代码,包括 500 毫秒的睡眠,始终将输出写入 snt2cooc
命令的文件:
cmdStr := "snt2cooc"
args := []string{srcVocab, tgtVocab, sntPath}
cmd := exec.Command(cmdStr, args...)
stdout, err := cmd.StdoutPipe()
time.Sleep(500 * time.Millisecond)
if err != nil {
return err
}
err = cmd.Start()
if err != nil {
return err
}
out := bufio.NewScanner(stdout)
for out.Scan() {
outfile.Write(out.Bytes())
outfile.WriteString("\n")
}
if err := out.Err(); err != nil {
return err
}
这向我证明存在一些竞争条件,Go 程序在所有输出写入文件之前退出。我为这个问题添加了赏金,希望有人可以 1) 解释为什么会发生这种情况,以及 2) 提供一种非 hacky 方式(即 500 毫秒睡眠)来修复它。
问题不在于 SNTtoCooc
的内部,而是您如何使用 cmd.Stdout
:
func anyWrite(args []string, outpath string) error {
outfile, err := os.Create(outpath)
if err != nil {
return err
}
defer outfile.Close()
// I use simple "echo" here
cmd := exec.Command("echo", args...)
stdout, err := cmd.Output()
if err != nil {
return err
}
// Use this instead of cmd.Stdout seems to solve the problem
outfile.Write(stdout)
return nil
}
func main() {
args := []string{"Line 1", "Line 2", "Line 3"}
if err := anyWrite(args, "./outfile.txt"); err != nil {
panic(err)
}
}
根据 os/exec
Stdout and Stderr specify the process's standard output and error. If either is nil, Run connects the corresponding file descriptor to the null device (os.DevNull).
首先,清理你的代码。
cmd.Stderr = os.DevNull
,所以你忽略stderr。 Stdout 和 Stderr 指定进程的标准输出和错误。如果任一个为 nil,运行 将相应的文件描述符连接到空设备 (os.DevNull)。
cmd.Wait()
returnserror,
你无视吧。 func (c *Cmd) Wait() error
.
Wait
等待命令退出。它必须由 Start.
启动 你使用 Run,
而不是 Start.
当您 运行 这段代码时,您会得到什么输出?
failure.go
:
package main
import (
"fmt"
"os"
"os/exec"
)
func main() {
err := SNTToCOOC("file1.vcb", "file2.vcb", "file3.snt", "out.txt")
if err != nil {
fmt.Println(err)
}
}
func SNTToCOOC(srcVocab, tgtVocab, sntPath, outpath string) error {
outfile, err := os.Create(outpath)
if err != nil {
return err
}
defer outfile.Close()
cmdStr := "snt2cooc"
args := []string{srcVocab, tgtVocab, sntPath}
cmd := exec.Command(cmdStr, args...)
cmd.Stdout = outfile
cmd.Stderr = os.Stderr
err = cmd.Run()
if err != nil {
return err
}
return err
}
运行:
$ rm -f out.txt && go run failure.go && cat out.txt
此外,当您 运行 这段代码用 cmd.Stdout = os.Stdout
代替 cmd.Stdout = outfile
时,您会得到什么输出。