我想用Python将mime / multipart消息写到标准输出,并使用mime/multipart
包在Golang中读取该消息。这只是一个学习练习。
我尝试模拟this example。
output.py
#!/usr/bin/env python2.7
import sys
s = "--foo\r\nFoo: one\r\n\r\nA section\r\n" +"--foo\r\nFoo: two\r\n\r\nAnd another\r\n" +"--foo--\r\n"
print s
main.go
package main
import (
"io"
"os/exec"
"mime/multipart"
"log"
"io/ioutil"
"fmt"
"sync"
)
var wg sync.WaitGroup
func main() {
pr,pw := io.Pipe()
defer pw.Close()
cmd := exec.Command("python","output.py")
cmd.Stdout = pw
mr := multipart.NewReader(pr,"foo")
wg.Add(1)
go func() {
defer wg.Done()
for {
p, err := mr.NextPart()
if err == io.EOF {
fmt.Println("EOF")
return
}
if err != nil {
log.Fatal(err)
}
slurp, err := ioutil.ReadAll(p)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Part : %q\n", slurp)
return
}
}()
if err := cmd.Start(); err != nil {
log.Fatal(err)
}
cmd.Wait()
wg.Wait()
}
go run main.go
的输出:
fatal error: all goroutines are asleep - deadlock!
有关StackOverflow上此主题的其他答案与通道未关闭有关,但我什至没有使用通道。我了解到某个地方存在无限循环或类似现象,但我看不到它。
答案 0 :(得分:0)
尝试类似的操作(在下面的说明中):
package main
import (
"fmt"
"io"
"io/ioutil"
"log"
"mime/multipart"
"os"
"os/exec"
"sync"
"github.com/pkg/errors"
)
func readCommand(cmdStdout io.ReadCloser, wg *sync.WaitGroup, resc chan<- []byte, errc chan<- error) {
defer wg.Done()
defer close(errc)
defer close(resc)
mr := multipart.NewReader(cmdStdout, "foo")
for {
part, err := mr.NextPart()
if err != nil {
if err == io.EOF {
fmt.Println("EOF")
} else {
errc <- errors.Wrap(err, "failed to get next part")
}
return
}
slurp, err := ioutil.ReadAll(part)
if err != nil {
errc <- errors.Wrap(err, "failed to read part")
return
}
resc <- slurp
}
}
func main() {
cmd := exec.Command("python", "output.py")
cmd.Stderr = os.Stderr
pr, err := cmd.StdoutPipe()
if err != nil {
log.Fatal(err)
}
var wg sync.WaitGroup
wg.Add(1)
resc := make(chan []byte)
errc := make(chan error)
go readCommand(pr, &wg, resc, errc)
if err := cmd.Start(); err != nil {
log.Fatal(err)
}
for {
select {
case err, ok := <-errc:
if !ok {
errc = nil
break
}
if err != nil {
log.Fatal(errors.Wrap(err, "error from goroutine"))
}
case res, ok := <-resc:
if !ok {
resc = nil
break
}
fmt.Printf("Part from goroutine: %q\n", res)
}
if errc == nil && resc == nil {
break
}
}
cmd.Wait()
wg.Wait()
}
无特殊顺序:
io.Pipe()
作为命令的Stdout
,而是询问命令的StdoutPipe()
。 cmd.Wait()
将确保它为您关闭。cmd.Stderr
设置为os.Stderr
,以便可以看到Python程序生成的错误。
WaitGroup
设置为全局变量;将对它的引用传递给goroutine。log.Fatal()
进入,而是创建一个错误通道以将错误传达回main()
。main()
,而不是在goroutine中打印结果。multipart.Reader()
,因为这是我们代码中唯一使用它的部分。Wrap()
包中的errors
将上下文添加到错误消息中。当然,这与您的问题无关,但是是个好习惯。 for { select { ... } }
部分可能令人困惑。 This是我发现的介绍这一概念的文章。基本上,select
允许我们从当前可读的这两个通道(resc
和errc
)中读取任何一个,然后在关闭通道时将它们分别设置为nil
。当两个通道均为nil
时,循环退出。这样一来,我们就可以处理“结果或错误”。
编辑:作为johandalabacka said on the Golang Forum,看来这里的主要问题是Windows上的Python向输出添加了额外的\r
,问题是您的Python程序应省略输出字符串中的\r
或sys.stdout.write()
而不是print()
。输出也可以在Golang端进行清理,但是,除了无法在不修改Python端的情况下正确解析之外,此答案仍将改善程序的并发机制。