func (i *cliUi) Input(opts *ui.InputOpts) (string, error) { // 如何设置了环境变量,我们就不询问提示input if value := opts.EnvVarValue(); value != "" { return value, nil } r := i.Reader w := i.Writer if r == nil { r = defaultInputReader } if w == nil { w = defaultInputWriter } if r == nil { r = os.Stdin } if w == nil { w = os.Stdout } // 确保我们只在第一次询问input.Terraform应该确保这个 // 但是不会破坏验证 i.l.Lock() defer i.l.Unlock() // 如果终止,那么就不询问input if i.interrupted { return "", errors.New("中断") } // 监听中断操作,那么就不再询问input sigCh := make(chan os.Signal, 1) signal.Notify(sigCh, os.Interrupt) defer signal.Stop(sigCh) // 格式化询问输出 var buf bytes.Buffer buf.WriteString("[reset]") buf.WriteString(fmt.Sprintf("[bold]%s[reset]\n", opts.Query)) if opts.Description != "" { s := bufio.NewScanner(strings.NewReader(opts.Description)) for s.Scan() { buf.WriteString(fmt.Sprintf(" %s\n", s.Text())) } buf.WriteString("\n") } if opts.Default != "" { buf.WriteString(" [bold]Default:[reset] ") buf.WriteString(opts.Default) buf.WriteString("\n") } buf.WriteString(" [bold]输入一个值:[reset] ") // 询问用户输入 if _, err := fmt.Fprint(w, ui.Colorize(buf.String())); err != nil { return "", err } // 监听goroutine输入。这允许我们中断 result := make(chan string, 1) if opts.Hide { f, ok := r.(*os.File) if !ok { return "", fmt.Errorf("必须读取一个文件") } line, err := password.Read(f) if err != nil { return "", err } result <- line } else { go func() { var line string if _, err := fmt.Fscanln(r, &line); err != nil { log.Printf("[ERR] UIInput扫描错误: %s", err) } result <- line }() } select { case line := <-result: fmt.Fprint(w, "\n") if line == "" { line = opts.Default } return line, nil case <-sigCh: // 新起一行 fmt.Fprintln(w) // Mark that we were interrupted so future Ask calls fail. i.interrupted = true return "", errors.New("中断") } }
func (i *cliUi) Input(opts *ui.InputOpts) (string, error) { // If any of the configured EnvVars are set, we don't ask for input. if value := opts.EnvVarValue(); value != "" { return value, nil } r := i.Reader w := i.Writer if r == nil { r = defaultInputReader } if w == nil { w = defaultInputWriter } if r == nil { r = os.Stdin } if w == nil { w = os.Stdout } // Make sure we only ask for input once at a time. Terraform // should enforce this, but it doesn't hurt to verify. i.l.Lock() defer i.l.Unlock() // If we're interrupted, then don't ask for input if i.interrupted { return "", errors.New("interrupted") } // Listen for interrupts so we can cancel the input ask sigCh := make(chan os.Signal, 1) signal.Notify(sigCh, os.Interrupt) defer signal.Stop(sigCh) // Build the output format for asking var buf bytes.Buffer buf.WriteString("[reset]") buf.WriteString(fmt.Sprintf("[bold]%s[reset]\n", opts.Query)) if opts.Description != "" { s := bufio.NewScanner(strings.NewReader(opts.Description)) for s.Scan() { buf.WriteString(fmt.Sprintf(" %s\n", s.Text())) } buf.WriteString("\n") } if opts.Default != "" { buf.WriteString(" [bold]Default:[reset] ") buf.WriteString(opts.Default) buf.WriteString("\n") } buf.WriteString(" [bold]Enter a value:[reset] ") // Ask the user for their input if _, err := fmt.Fprint(w, ui.Colorize(buf.String())); err != nil { return "", err } // Listen for the input in a goroutine. This will allow us to // interrupt this if we are interrupted (SIGINT) result := make(chan string, 1) if opts.Hide { f, ok := r.(*os.File) if !ok { return "", fmt.Errorf("reader must be a file") } line, err := password.Read(f) if err != nil { return "", err } result <- line } else { go func() { var line string if _, err := fmt.Fscanln(r, &line); err != nil { log.Printf("[ERR] UIInput scan err: %s", err) } result <- line }() } select { case line := <-result: fmt.Fprint(w, "\n") if line == "" { line = opts.Default } return line, nil case <-sigCh: // Print a newline so that any further output starts properly // on a new line. fmt.Fprintln(w) // Mark that we were interrupted so future Ask calls fail. i.interrupted = true return "", errors.New("interrupted") } }