Golang读取管道输入后无法正常发起后续用户交互提示问题
问题根因
通过管道向程序传递输入时,os.Stdin会被绑定到管道的读端而非当前终端的输入流。管道内的内容读取完成后,流就会到达EOF状态,后续survey库默认仍从os.Stdin读取输入,会直接触发EOF错误,无法接收用户在终端输入的确认指令。
该需求可实现,核心逻辑是读取完管道内容后,将后续交互的输入输出流重新指向当前进程绑定的控制终端(tty)。
修复方案
按以下逻辑调整代码即可:
- 检测到管道输入时,一次性将管道内的所有内容读取到变量中,释放默认的Stdin句柄
- 触发交互式确认前,先判断是否传入了跳过确认的
-y参数:如果传入则直接走后续业务逻辑;如果未传入,直接打开当前系统的终端设备文件,将其指定为survey库的输入输出源,绕过已经处于EOF状态的管道Stdin - 类Unix系统(Linux、macOS)的终端设备路径为
/dev/tty,Windows系统对应路径为con
修正后示例代码
package main import ( "fmt" "io" "os" "strings" "github.com/AlecAivazis/survey/v2" "github.com/AlecAivazis/survey/v2/terminal" ) func main() { value := "" autoConfirm := false // 对应-y参数逻辑,实际使用时可通过flag包解析参数赋值 isPiped, err := isPipedInput() if err != nil { panic(err) } if isPiped { // 一次性读取管道内全部内容 pipeContent, err := io.ReadAll(os.Stdin) if err != nil { panic(err) } value = strings.TrimSpace(string(pipeContent)) if value == "" { panic(fmt.Errorf("pipe input must not be empty")) } } else { // 非管道场景走正常交互流程 err = survey.AskOne(&survey.Input{Message: "Enter value followed by [Enter]:"}, &value) if err != nil { panic(err) } if value == "" { panic(fmt.Errorf("input value must not be empty")) } } // 传入-y参数则跳过确认流程 if !autoConfirm { // 打开当前控制终端作为交互输入输出源 tty, err := os.OpenFile("/dev/tty", os.O_RDWR, 0) if err != nil { panic(fmt.Errorf("failed to open tty for interactive prompt: %w", err)) } defer tty.Close() // 指定survey从tty读写,不再使用已到EOF的管道Stdin shouldProceed := false err = survey.AskOne( &survey.Confirm{Message: "Are you sure?"}, &shouldProceed, survey.WithStdio( terminal.FileReader{Fd: tty}, terminal.FileWriter{Fd: tty}, tty, ), ) if err != nil { panic(err) } if !shouldProceed { fmt.Println("Not proceeding...", value) return } } fmt.Println("Proceeding!", value) } func isPipedInput() (bool, error) { fi, err := os.Stdin.Stat() if err != nil { return false, err } return (fi.Mode() & os.ModeCharDevice) == 0, nil }
编译&验证
编译命令保持不变:
go build -o main
三个核心场景均能符合预期:
- 直接执行
./main:依次弹出输入值提示、确认提示,交互逻辑和原版本完全一致 - 执行
echo "foo" | ./main:自动读取管道传入的foo作为输入值,紧接着弹出确认提示,可正常接收用户输入的y/N指令,无EOF报错 - 完成
-y参数的解析逻辑(示例代码中autoConfirm变量对应该开关)后,执行echo "foo" | ./main -y:全程无人工交互,直接输出执行结果
若需要兼容Windows系统,只需将代码中打开
/dev/tty的逻辑做系统分支判断,Windows下替换为打开con设备即可。
内容的提问来源于stack exchange,提问作者Nick
相关产品推荐
相关产品推荐

