代码示例:
package main
import (
"io"
"os"
"os/signal"
"sync"
"syscall"
)
func main() {
sigintCh := make(chan os.Signal, 1)
signal.Notify(sigintCh, syscall.SIGINT, syscall.SIGTERM)
var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
io.Copy(os.Stdout, os.Stdin)
}()
<-sigintCh
os.Stdin.Close()
wg.Wait()
}
如果运行此示例并尝试通过^C
中断,它将等待任何输入,并且仅在向 stdin 发送内容后停止(例如,只需按回车键)。
我希望关闭 Stdin 就像发送 EOF 一样,但它不起作用。
关闭os.Stdin
将导致io.Copy
在下次读取时返回错误file already closed
(CTRL-C
后,请尝试按Enter
)。
如 File.Close 文档中所述:
"关闭"关闭文件,使其不可用于 I/O。
您不能通过关闭 EOF(或任何其他方式)来强制从os.Stdin
返回 EOF。相反,您需要包装os.Stdin
并实现您自己的有条件返回EOF
的Read
方法,或者在循环中读取有限数量的字节。
您可以在此 golang-nuts 线程上看到更多讨论和可能的解决方法。
您可以在不关闭源端的情况下中断io.Copy
- 通过传递已包装有逻辑的io.Reader
,该逻辑采用此处概述的可取消context.Context
。
像这样修改你上面的程序:
ctx, cancel := context.WithCancel(context.Background())
go func() {
defer wg.Done()
r := NewReader(ctx, os.Stdin) // wrap io.Reader to make it context-aware
_, err := io.Copy(os.Stdout, r)
if err != nil {
// context.Canceled error if interrupted
}
}()
<-sigintCh
cancel() // canceling context will interrupt io.Copy operation
您可以从外部包(如 github.com/jbenet/go-context/io)导入NewReader
,也可以从上面的博客链接中内联代码段:
type readerCtx struct {
ctx context.Context
r io.Reader
}
func (r *readerCtx) Read(p []byte) (n int, err error) {
if err := r.ctx.Err(); err != nil {
return 0, err
}
return r.r.Read(p)
}
// NewReader gets a context-aware io.Reader.
func NewReader(ctx context.Context, r io.Reader) io.Reader {
return &readerCtx{ctx: ctx, r: r}
}