如何在请求新输入时终止控制台输入请求

huangapple go评论88阅读模式
英文:

How to terminate a console input request when a new input is requested

问题

我需要在请求新的控制台输入时终止现有的控制台输入请求。以下代码尝试使用通道关闭现有请求,但似乎无法终止输入请求。

package main

import (
	"bufio"
	"fmt"
	"log"
	"os"
	"strings"
	"time"
)

func main() {
	go Confirm("你是一个程序员吗?")
	time.Sleep(2 * time.Second)
	Confirm("你喜欢Go语言吗?")
}

var cancelChannel chan struct{}

func Confirm(s string) bool {
	// 检查通道类型是否持有值,然后关闭通道以删除先前的确认输入
	if cancelChannel != nil {
		fmt.Println("将关闭通道")
		close(cancelChannel)
	}
	cancelChannel = make(chan struct{})
	reader := bufio.NewReader(os.Stdin)
	for {
		fmt.Printf("%s [y/n]: ", s)

		response, err := reader.ReadString('\n')
		if err != nil {
			log.Fatal(err)
		}

		response = strings.ToLower(strings.TrimSpace(response))

		if response == "y" || response == "yes" {
			return true
		} else if response == "n" || response == "no" {
			return false
		}

		if _, ok := <-cancelChannel; !ok {
			fmt.Println("通道已关闭")
			return false
		}

	}
}

希望对你有帮助!

英文:

I need to terminate an existing console input request when a new one is requested. The following code is an attempt to close an existing request using a channel but it does not seem to terminate the input request.

package main

import (
	&quot;bufio&quot;
	&quot;fmt&quot;
	&quot;log&quot;
	&quot;os&quot;
	&quot;strings&quot;
	&quot;time&quot;
)

func main() {
	go Confirm(&quot;you are a programmer, aint you?&quot;)
	time.Sleep(2 * time.Second)
	Confirm(&quot;do you love go?&quot;)
}

var cancelChannel chan struct{}

func Confirm(s string) bool {
	//check if channel type holds a value then close the channel to remove previous confirmation input
	if cancelChannel != nil {
		fmt.Println(&quot;channel to be closed&quot;)
		close(cancelChannel)
	}
	cancelChannel = make(chan struct{})
	reader := bufio.NewReader(os.Stdin)
	for {
		fmt.Printf(&quot;%s [y/n]: &quot;, s)

		response, err := reader.ReadString(&#39;\n&#39;)
		if err != nil {
			log.Fatal(err)
		}

		response = strings.ToLower(strings.TrimSpace(response))

		if response == &quot;y&quot; || response == &quot;yes&quot; {
			return true
		} else if response == &quot;n&quot; || response == &quot;no&quot; {
			return false
		}

		if _, ok := &lt;-cancelChannel; !ok {
			fmt.Println(&quot;channel closed&quot;)
			return false
		}

	}
}

答案1

得分: 1

如@JimB在评论中提到的,你不能中断stdin上的读取,尽管有一种不太正规的技巧可以实现。可以使用syscall来复制os.Stdin文件描述符(不推荐),并将其作为非阻塞文件打开。

package main

import (
	"bytes"
	"context"
	"errors"
	"fmt"
	"io"
	"io/fs"
	"io/ioutil"
	"os"
	"syscall"
	"time"
)

func setNonblock(f *os.File) error {
	c, err := f.SyscallConn()
	if err != nil {
		return err
	}

	var err2 error
	err = c.Control(func(fd uintptr) {
		err2 = syscall.SetNonblock(int(fd), true)
	})
	if err != nil {
		return err
	}
	return err2
}

func nonBlockingFile(f *os.File) (*os.File, error) {
	if err := setNonblock(f); err != nil {
		return nil, err
	}
	fd, err := syscall.Dup(int(f.Fd()))
	if err != nil {
		return nil, err
	}
	f2 := os.NewFile(uintptr(fd), f.Name())
	return f2, nil
}

func read(ctx context.Context, f *os.File) (io.Reader, error) {
	r, err := nonBlockingFile(f)
	if err != nil {
		return nil, err
	}
	go func() {
		defer r.Close()
		<-ctx.Done()
	}()
	buff := bytes.NewBuffer([]byte{})
	for {
		_, err := io.Copy(buff, r)
		if err != nil {
			if errors.Is(err, fs.ErrClosed) {
				break
			}
			panic(err)
		}
	}
	return buff, nil
}

func main() {
	ctx1, cancel := context.WithCancel(context.Background())
	go func() {
		time.Sleep(time.Second * 2)
		cancel()

	}()
	buf1, err := read(ctx1, os.Stdin)
	if err != nil {
		panic(err)
	}
	ctx2, _ := context.WithTimeout(context.Background(), time.Second*2)
	buf2, err := read(ctx2, os.Stdin)

	fmt.Println("buf1")
	fmt.Println(ioutil.ReadAll(buf1))
	fmt.Println("buf2")
	fmt.Println(ioutil.ReadAll(buf2))
}
英文:

As @JimB mentioned in comment you can't interrupt read on stdin although there is kinda shady trick how you can achieve it. It's possible to duplicate os.Stdin file descriptor using syscall (not recommended) and open it as non blocking file.

package main

import (
	&quot;bytes&quot;
	&quot;context&quot;
	&quot;errors&quot;
	&quot;fmt&quot;
	&quot;io&quot;
	&quot;io/fs&quot;
	&quot;io/ioutil&quot;
	&quot;os&quot;
	&quot;syscall&quot;
	&quot;time&quot;
)

func setNonblock(f *os.File) error {
	c, err := f.SyscallConn()
	if err != nil {
		return err
	}

	var err2 error
	err = c.Control(func(fd uintptr) {
		err2 = syscall.SetNonblock(int(fd), true)
	})
	if err != nil {
		return err
	}
	return err2
}

func nonBlockingFile(f *os.File) (*os.File, error) {
	if err := setNonblock(f); err != nil {
		return nil, err
	}
	fd, err := syscall.Dup(int(f.Fd()))
	if err != nil {
		return nil, err
	}
	f2 := os.NewFile(uintptr(fd), f.Name())
	return f2, nil
}

func read(ctx context.Context, f *os.File) (io.Reader, error) {
	r, err := nonBlockingFile(f)
	if err != nil {
		return nil, err
	}
	go func() {
		defer r.Close()
		&lt;-ctx.Done()
	}()
	buff := bytes.NewBuffer([]byte{})
	for {
		_, err := io.Copy(buff, r)
		if err != nil {
			if errors.Is(err, fs.ErrClosed) {
				break
			}
			panic(err)
		}
	}
	return buff, nil
}

func main() {
	ctx1, cancel := context.WithCancel(context.Background())
	go func() {
		time.Sleep(time.Second * 2)
		cancel()
		
	}()
	buf1, err := read(ctx1, os.Stdin)
	if err != nil {
		panic(err)
	}
	ctx2, _ := context.WithTimeout(context.Background(), time.Second*2)
	buf2, err := read(ctx2, os.Stdin)

	fmt.Println(&quot;buf1&quot;)
	fmt.Println(ioutil.ReadAll(buf1))
	fmt.Println(&quot;buf2&quot;)
	fmt.Println(ioutil.ReadAll(buf2))
}

答案2

得分: 0

继续探索Go语言提供的简洁性,可以查看https://pkg.go.dev/context#WithCancel

你可以使用context.WithCancel创建一个返回CancelFunc的上下文。如果你想要终止操作,可以执行cancel函数。

这是一种良好的实践方式,另外一种方法是使用os.Exit(0)来强制退出。

英文:

Go on and explore the simplicity offer by go
https://pkg.go.dev/context#WithCancel

You can have a context that returning CancelFunc then you use context.WithCancel.
And execute cancel func if you want to terminate.

This is the good practice way, you can also do a dirty os.Exit(0) in another case.

huangapple
  • 本文由 发表于 2022年4月7日 19:56:08
  • 转载请务必保留本文链接:https://go.coder-hub.com/71781753.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定