我有一个命令我必须通过操作系统运行,我们称之为“登录”,这是交互式的,因此需要我从stdin读取并将其传递给命令的stdin管道才能使其工作正确。唯一的问题是从stdin读取的goroutine块,我无法找到一种方法来取消Go中的Reader,以使其不挂起阻塞调用。例如,从用户的角度来看,在命令看起来好像完成后,您仍然有机会再次写入stdin(然后goroutine将移过阻塞读取并退出)
理想情况下,我希望避免解析命令的StdoutPipe的输出,因为如果login
命令的字符串发生变化,我的代码会很脆弱并容易出错。
loginCmd := exec.Command("login")
stdin , err := loginCmd.StdinPipe()
if err != nil {
return err
}
out, err := loginCmd.StdoutPipe()
if err != nil {
return err
}
if err := loginCmd.Start(); err != nil {
return err
}
ctx, cancel := context.WithCancel(context.TODO())
var done sync.WaitGroup
done.Add(1)
ready := make(chan bool, 1)
defer cancel()
go func(ctx context.Context) {
reader := bufio.NewReader(os.Stdin)
for {
select {
case <- ctx.Done():
done.Done()
return
default:
//blocks on this line, if a close can unblock the read, then it should exit normally via the ctx.Done() case
line, err :=reader.ReadString('\n')
if err != nil {
fmt.Println("Error: ", err.Error())
}
stdin.Write([]byte(line))
}
}
}(ctx)
var bytesRead = 4096
output := make([]byte, bytesRead)
reader := bufio.NewReader(out)
for err == nil {
bytesRead, err = reader.Read(output)
if err != nil && err != io.EOF {
return err
}
fmt.Printf("%s", output[:bytesRead])
}
if err := loginCmd.Wait(); err != nil {
return err
}
cancel()
done.Wait()