英文:
Reading Stdout from a subprocess
问题
我正在尝试从Golang生成一个子进程。目标是逐行读取和处理输入。以下是我正在尝试使其工作的代码:
func readStuff(scanner *bufio.Scanner) {
for scanner.Scan() {
fmt.Println("Performed Scan")
fmt.Println(scanner.Text())
}
if err := scanner.Err(); err != nil {
fmt.Fprintln(os.Stderr, "reading standard input:", err)
}
}
func main() {
cmd := exec.Command("/usr/local/bin/pocketsphinx_continuous", "-inmic", "yes")
out, err := cmd.StdoutPipe()
err = cmd.Start()
checkError(err)
scanner := bufio.NewScanner(out)
fmt.Println("Scanner created")
defer cmd.Wait()
go readStuff(scanner)
}
在这个例子中,会打印出"Scanner created",但之后没有任何输出。
然而,运行以下命令会产生我期望的输出:
/usr/local/bin/pocketsphinx_continuous -inmic yes 1>out.txt
将代码修改为直接复制到stdout
也可以正常工作:
cmd := exec.Command("/usr/local/bin/pocketsphinx_continuous", "-inmic", "yes")
cmd.Stdout = os.Stdout
我错过了什么导致无法读取输出?
英文:
I am attempting to spawn a subprocess from Golang. The goal is to read and process the input line-by-line. Here is what I am trying to get working:
func readStuff(scanner *bufio.Scanner) {
for scanner.Scan() {
fmt.Println("Performed Scan")
fmt.Println(scanner.Text())
}
if err := scanner.Err(); err != nil {
fmt.Fprintln(os.Stderr, "reading standard input:", err)
}
}
func main() {
cmd := exec.Command("/usr/local/bin/pocketsphinx_continuous", "-inmic", "yes")
out, err := cmd.StdoutPipe()
err = cmd.Start()
checkError(err)
scanner := bufio.NewScanner(out)
fmt.Println("Scanner created")
defer cmd.Wait()
go readStuff(scanner)
}
In this example, "Scanner created" is printed, but nothing happens after that.
Running this command however does result in what I am expecting to be printed to :
/usr/local/bin/pocketsphinx_continuous -inmic yes 1>out.txt
And modifying the code to directly copy to stdout
works as well:
cmd := exec.Command("/usr/local/bin/pocketsphinx_continuous", "-inmic", "yes")
cmd.Stdout = os.Stdout
What am I missing that is keeping me from reading the output?
答案1
得分: 6
有几个事情你可能想要检查。
-
没有检查
cmd.StdoutPipe()
返回的错误代码。应该进行检查。 -
pocketsphinx_continuous
命令需要提供-hmm
和-dict
参数。否则,它将失败,并且所有的输出实际上都被发送到stderr
而不是stdout
。在这里,你只读取了stdout
,但没有内容可读取。 -
在确定从
stdout
中读取了所有数据之前,不应该调用cmd.Wait()
。结果是不确定的(实际上,这是一种竞争条件)。请查阅关于os/exec
包的文档。如果你绝对需要在 goroutine 中进行解析,你需要在调用cmd.Wait()
之前与 goroutine 的结束进行同步。例如,你可以将函数写成:func readStuff(scanner *bufio.Scanner, stop chan bool) { // 扫描代码 // ... stop <- true }
并将主要代码写成:
stop := make(chan bool)
go readStuff(scanner, stop)
<-stop
cmd.Wait()
英文:
There are multiple things you may want to check.
-
The error code returned by
cmd.StdoutPipe()
is not checked. It should be. -
The
pocketsphinx_continuous
command requires the-hmm
and-dict
arguments to be provided. Otherwise, it will fail, and all the output is actually sent tostderr
and notstdout
. Here, you read onlystdout
, but there is nothing to read. -
You should not call
cmd.Wait()
before being sure all the data have been read fromstdout
. The result is non deterministic (actually, it is a race condition). Check the documentation about theos/exec
package. If you absolutely need the parsing to be done in a goroutine, you need to synchronize with the end of the goroutine beforecmd.Wait()
is called. For instance, you could write the function as:func readStuff(scanner *bufio.Scanner, stop chan bool) { // Scanning code // ... stop<-true }
and the main code as:
stop := make(chan bool)
go readStuff(scanner,stop)
<-stop
cmd.Wait()
答案2
得分: 1
看起来你不需要执行go readStuff(scanner)
,因为cmd.Start()
会自动创建一个系统进程。所以我认为只需要执行readStuff(scanner)
就足够了,不需要为此创建一个goroutine。
英文:
Seems you need not
go readStuff(scanner)
because of
cmd.Start()
do system fork itself
so just
readStuff(scanner)
would be enough to my mind
(not spawning gorouting for that)
答案3
得分: 1
这似乎工作得很好,并且可以使用go readStuff(scanner)
或者只是readStuff(scanner)
来调用。我认为这种用法实际上并不需要使用goroutine,但是不知道实际的上下文。
package main
import (
"bufio"
"fmt"
"os"
"os/exec"
)
func readStuff(scanner *bufio.Scanner) {
for scanner.Scan() {
fmt.Println("Performed Scan")
fmt.Println(scanner.Text())
}
if err := scanner.Err(); err != nil {
fmt.Fprintln(os.Stderr, "reading standard input:", err)
}
}
func main() {
cmd := exec.Command("/Users/rfay/bin/junk.sh")
out, err := cmd.StdoutPipe()
err = cmd.Start()
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to start err=%v", err)
os.Exit(1)
}
scanner := bufio.NewScanner(out)
fmt.Println("Scanner created")
defer cmd.Wait()
go readStuff(scanner)
}
这是我用于测试的junk.sh
脚本。
#!/bin/bash
for i in `seq 1 10`;
do
echo $i
sleep 1
done
英文:
This seems to work fine, and it works with go readStuff(scanner)
and also with just readStuff(scanner)
- I don't think that this usage actually calls for a goroutine, but don't know the actual context.:
package main
import (
"bufio"
"fmt"
"os"
"os/exec"
)
func readStuff(scanner *bufio.Scanner) {
for scanner.Scan() {
fmt.Println("Performed Scan")
fmt.Println(scanner.Text())
}
if err := scanner.Err(); err != nil {
fmt.Fprintln(os.Stderr, "reading standard input:", err)
}
}
func main() {
cmd := exec.Command("/Users/rfay/bin/junk.sh")
out, err := cmd.StdoutPipe()
err = cmd.Start()
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to start err=%v", err)
os.Exit(1)
}
scanner := bufio.NewScanner(out)
fmt.Println("Scanner created")
defer cmd.Wait()
go readStuff(scanner)
}
This is the junk.sh I used for testing.
#!/bin/bash
for i in `seq 1 10`;
do
echo $i
sleep 1
done
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论