猿问

在 Go 中获取管道状态

我无法在 Go (1.5) 中获得管道状态。

mkfifo创建的管道上写入时,我尝试获取此输出管道的状态:

  • 使用Write退货状态EPIPE

  • 使用Write返回状态EPIPEsignal.IgnoreSIGPIPE(以防万一)

  • signal.Notify在 SIGPIPE 上使用

我知道:

  • EPIPE 永远不会回来

  • 当我使用时kill -13,信号处理程序被称为:“得到信号:管道损坏”

  • 当我ctrl-c阅读时,信号处理程序没有被调用,我的程序退出并输出:“信号:管道损坏”

请你指出我的错误好吗?

// tee.go

package main


import (

    "fmt"

    "os"

    "os/signal"

    "syscall"


    sys "golang.org/x/sys/unix"

)


// wait for a signal and print it

func handleSignal(csig chan os.Signal) {

    for {

        fmt.Println("Wait signal")

        s := <-csig

        fmt.Println("Got signal:", s)

    }

}


func main() {

    csig := make(chan os.Signal, 1)


    // `kill -13` outputs "Got signal: broken pipe" => ok

    signal.Notify(csig, sys.SIGPIPE)


    // OR disable the previous `Notify` just to be sure ?

    // maybe it will help to get the EPIPE error status on `Write` ?

    //signal.Ignore(sys.SIGPIPE)


    go handleSignal(csig)


    // open previously created named pipe (`mkfifo /tmp/test`)

    pipe, _ := os.OpenFile("/tmp/test", os.O_WRONLY, 0)


    for {

        _, err := pipe.Write([]byte("foo\n"))

        if err == syscall.EPIPE {

            // never called => ko

            fmt.Println("EPIPE error")

        }

    }

}

注意:作为一个简单的 Go 练习,我尝试实现一个命令,它的行为几乎类似于tee -a <a_file>(将 stdin 打印到 stdout 和<a_file>)


慕姐8265434
浏览 148回答 1
1回答

犯罪嫌疑人X

返回的错误不是普通的syscall.Error,而是包含在*os.PathError代码中的以下变体中:package mainimport (&nbsp; &nbsp; "fmt"&nbsp; &nbsp; "os"&nbsp; &nbsp; "syscall")func main() {&nbsp; &nbsp; // open previously created named pipe (`mkfifo /tmp/test`)&nbsp; &nbsp; pipe, _ := os.OpenFile("/tmp/test", os.O_WRONLY, 0)&nbsp; &nbsp; for {&nbsp; &nbsp; &nbsp; &nbsp; n, err := pipe.Write([]byte("foo\n"))&nbsp; &nbsp; &nbsp; &nbsp; fmt.Printf("write: n=%v, err=(%T) %[2]v\n", n, err)&nbsp; &nbsp; &nbsp; &nbsp; if err == syscall.EPIPE {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; fmt.Println("EPIPE error")&nbsp; &nbsp; &nbsp; &nbsp; } else if perr, ok := err.(*os.PathError); ok {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; fmt.Printf("op: %q; path=%q; err=(%T) %[3]q\n",&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; perr.Op, perr.Path, perr.Err)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if perr.Err == syscall.EPIPE {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; fmt.Println("os.PathError.Err is EPIPE")&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }}在mkfifo /tmp/test; head /tmp/test其他地方做完之后运行这个给我:write: n=4, err=(<nil>) <nil>[… repeated nine more times, as the head command reads ten lines …]write: n=0, err=(*os.PathError) write /tmp/test: broken pipeop: "write"; path="/tmp/test"; err=(syscall.Errno) "broken pipe"os.PathError.Err is EPIPE[… above three lines repeated nine more times …]signal: broken pipeExit 1在单个文件上返回十个管道错误后,Go runtine 停止捕获/阻塞 SIGPIPE 并让它通过您的程序杀死它。我不相信 Go 运行时让您捕获或忽略 SIGPIPE,因为它在内部使用该信号本身。所以有两件事:一,寻找syscall.EPIPE你需要检查*os.PathError如图所示,二,err != nil当你没有真正处理错误时不要继续。我不知道Go为什么这样处理SIGPIPE的细节;也许搜索 Go 的错误跟踪器和/或 go-nuts 列表可能有助于回答这个问题。随着 Go 1.5 对os/signal包的添加signal.Reset(syscall.SIGPIPE)(在任何signal.Notify调用之前)改变了这种行为。
随时随地看视频慕课网APP

相关分类

Go
我要回答