Using cgo, why does C output not 'survive' piping when golang's does?

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

Using cgo, why does C output not 'survive' piping when golang's does?

问题

我正在使用cgo来从golang中使用C代码进行实验,但在我的小型hello-world测试中,我遇到了一些我无法理解或找到更多信息的问题。

我从一个类似于我找到的示例开始进行简单的测试(https://github.com/draffensperger/go-interlang/blob/master/go_to_c/c_in_comment/main.go)

package main

import (
	"fmt"
	"unsafe"
)

/*
#import <stdio.h>
#import <stdlib.h>
*/
import "C"

func main() {
	go2c := "Printed from C.puts"
	var cstr *C.char = C.CString(go2c)
	defer C.free(unsafe.Pointer(cstr))
	C.puts(cstr)
	fmt.Printf("Printed from golang fmt\n")
}

这个简单的示例通过基本的cgo绑定,从golang(使用fmt.Printf)和原始的C(使用C.puts)将字符串回显到stdout。

当我直接在终端中运行时,我可以看到两行输出:

$ ./main
Printed from C.puts
Printed from golang fmt

但是,当我运行这个程序并将输出重定向到其他地方,比如通过管道到less,或者通过shell重定向到文件等等,我只能看到golang的输出:

./main | cat
Printed from golang fmt

当进行管道/重定向时,C.puts的内容会发生什么变化?

附加问题:这是cgo的一个怪异行为,还是我不知道的C标准库的怪异行为?这种行为是否有文档记录?我该如何自行调试(例如,是否有一种好的/可行的方法让我在每个块中“检查”FD1是什么)?

更新:如果相关的话,我正在使用go version go1.6.2 darwin/amd64

英文:

I'm experimenting with cgo to use C code from golang, but in my little hello-world test, I've ran into something I can't understand or find more information about.

I'm starting with a simple test similar to examples I've found

    package main
    
    import (
    	&quot;fmt&quot;
    	&quot;unsafe&quot;
    )
    
    /*
    #import &lt;stdio.h&gt;
    #import &lt;stdlib.h&gt;
    */
    import &quot;C&quot;
    
    func main() {
    	go2c := &quot;Printed from C.puts&quot;
    	var cstr *C.char = C.CString(go2c)
    	defer C.free(unsafe.Pointer(cstr))
    	C.puts(cstr)
    	fmt.Printf(&quot;Printed from golang fmt\n&quot;)
    }

This simple example just echoes strings to stdout from both golang (using fmt.Printf) and raw C (using C.puts) via the basic cgo binding.

When I run this directly in my terminal, I see both lines:

    $ ./main
    Printed from C.puts
    Printed from golang fmt

When I run this but redirect output in any way – pipe to less, shell redirection to a file, etc – I only see golang's output:

    ./main | cat
    Printed from golang fmt

What happens to the C.puts content when piping / redirecting?

Secondary questions: Is this a cgo quirk, or a c standard library quirk I'm not aware of? Is this behaviour documented? How would I go about debugging this on my own (e.g. is there a good/plausible way for me to 'inspect' what FD1 really is in each block?)

Update: If it's relevant, I'm using go version go1.6.2 darwin/amd64.

答案1

得分: 3

这是你所看到的 C 语言行为。

Go 语言不会缓冲 stdout,而在 C 语言中通常会进行缓冲。当 C 库检测到 stdout 是终端时,它可能会使用行缓冲,因此 puts 插入的额外 \n 会导致输出被显示出来。

你需要刷新 stdout 以确保获取所有的输出:

go2c := "Printed from C.puts"
var cstr *C.char = C.CString(go2c)
defer C.free(unsafe.Pointer(cstr))
C.puts(cstr)
C.fflush(C.stdout)
fmt.Printf("Printed from golang fmt\n")

参考资料:

https://stackoverflow.com/questions/1716296/why-does-printf-not-flush-after-the-call-unless-a-newline-is-in-the-format-strin

https://stackoverflow.com/questions/3723795/is-stdout-line-buffered-unbuffered-or-indeterminate-by-default

英文:

This is C behavior you're seeing.

Go does not buffer stdout, while in C it is usually buffered. When the C library detects stdout is a tty, it may use line buffering, so the additional \n inserted by puts will cause the output to be displayed.

You need to flush stdout to ensure you get all the output:

go2c := &quot;Printed from C.puts&quot;
var cstr *C.char = C.CString(go2c)
defer C.free(unsafe.Pointer(cstr))
C.puts(cstr)
C.fflush(C.stdout)
fmt.Printf(&quot;Printed from golang fmt\n&quot;)

See also

https://stackoverflow.com/questions/1716296/why-does-printf-not-flush-after-the-call-unless-a-newline-is-in-the-format-strin

https://stackoverflow.com/questions/3723795/is-stdout-line-buffered-unbuffered-or-indeterminate-by-default

答案2

得分: 1

C库的缓冲是按行进行的,因此在正确刷新之前(在C程序退出时完成),第一行可以保留在缓冲区中。你可以尝试刷新stdout,或者在第一个字符串中添加一个尾随的\n。如果添加\n后能正常工作吗?

英文:

The C library buffering is per line, so the first line can be left in the buffer before it is properly flushed (done at exit time in C programs). You can either try to flush stdout, or try adding a trailing \n in the first string. Does it work if you add the \n?

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

发表评论

匿名网友

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

确定