从用户读取输入时忽略 CTRL-C。

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

Read input from user while ignoring CTRL-C

问题

我想在Go语言中读取用户输入,并忽略用户通过捕获CTRL-C来终止我的进程的尝试。我创建了一个goroutine来捕获CTRL-C并写入一个通道。在这个阶段,我期望控制权返回到主goroutine中的case <-intr,但实际上并没有发生。

以下是运行的输出:

$ go run read.go 
输入文本:hello
输入的文本:hello

$ go run read.go 
输入文本:^C捕获到中断信号
退出信号处理程序..

无效的输入2
中断.. 继续
输入文本:^C
无效的输入2
输入文本:^C
无效的输入2
输入文本:^C^C^C^C
无效的输入2
输入文本:^C
无效的输入2
输入文本:^C
无效的输入2
输入文本: 

第一个CTRL-C被成功捕获,然后退出,似乎主goroutine正在执行r := bufio.NewReader(os.Stdin)

当我稍后将CTRL-C作为输入时,它只被视为文本,信号处理程序不会被调用。

我的代码如下(goplay链接:http://play.golang.org/p/LiKZMYGr00):

package main

import (
	"fmt"
	"bufio"
	"os"
	"os/signal"
	"syscall"
	"io"
	"strings"
)

func main() {
	c := make(chan os.Signal)
	intr := make(chan bool)

	signal.Notify(c, syscall.SIGINT)
	go func() {
		s := <-c
		fmt.Println("捕获到信号", s)
		fmt.Println("退出信号处理程序..")
		intr <- true
		return
	}()

breakOutOfHere:
	for {
		select {
		case <-intr:
			fmt.Println("中断.. 继续")
			continue
		default:
			fmt.Printf("输入文本:")
			r := bufio.NewReader(os.Stdin)
			text, ret := r.ReadString('\n')
			if ret == io.EOF {
				fmt.Println("无效的输入1")
				continue
			} else if ret == nil {
				text = strings.TrimSpace(text)
				if text == "" {
					fmt.Println("无效的输入2")
					continue
				} else {
					fmt.Printf("输入的文本:%s\n", text)
					break breakOutOfHere
				}
			}
		}
	}
}

希望对你有所帮助!

英文:

I'd like to read user-input in go and ignore the user's attempts to kill my process by trapping CTRL-C. I spin a goroutine to trap CTRL-C and also write to a channel. At this stage, I expect control to return to case &lt;-intr in the main goroutine, but that's not happening.

Here's output from a run:

$ go run read.go 
Enter text: hello
Entered:hello

$go run read.go 
Enter text: ^CGot signal interrupt
Exiting signal handler..

Invalid input2
Interrupted.. Continuing
Enter text: ^C
Invalid input2
Enter text: ^C
Invalid input2
Enter text: ^C^C^C^C
Invalid input2
Enter text: ^C
Invalid input2
Enter text: ^C
Invalid input2
Enter text: 

The first CTRL-C is trapped alright and soon after it exits, it appears that the main goroutine is executing r := bufio.NewReader(os.Stdin)

When I hit CTRL-C as input later, it is simply treated as text and the signal handler isn't invoked.

My code is below (goplay link: http://play.golang.org/p/LiKZMYGr00)

package main

import (&quot;fmt&quot;
	&quot;bufio&quot;
	&quot;os&quot;
	&quot;os/signal&quot;
	&quot;syscall&quot;
	&quot;io&quot;
	&quot;strings&quot;
)

func main() {
	c := make(chan os.Signal)
	intr := make(chan bool)

	signal.Notify(c,  syscall.SIGINT)
	go func() {
		s := &lt;-c
		fmt.Println(&quot;Got signal&quot;, s)
		fmt.Println(&quot;Exiting signal handler..&quot;)
		intr &lt;- true
		return
	}()

breakOutOfHere:
	for {
		select {
		case &lt;-intr:
			fmt.Println(&quot;Interrupted.. Continuing&quot;)
			continue
		default:
			fmt.Printf(&quot;Enter text: &quot;)
			r := bufio.NewReader(os.Stdin)
			text, ret := r.ReadString(&#39;\n&#39;)
			if ret == io.EOF {
				fmt.Println(&quot;Invalid input1&quot;)
				continue
			} else if ret == nil {
				text = strings.TrimSpace(text)
				if text == &quot;&quot; {
					fmt.Println(&quot;Invalid input2&quot;)
					continue
				} else {
					fmt.Printf(&quot;Entered:%s\n&quot;, text)
					break breakOutOfHere
				}
			}
		}
	}
}

答案1

得分: 4

signal.Notify会在进程接收到信号时将信号信息发送到指定的通道。但是在你的代码中,go协程在接收到第一个信号后就完成了,所以它无法再次捕获信号。

一种简单的方法是,在go协程中使用无限循环和select语句。

go func(c chan os.Signal, quit chan bool) {
    for {
        select {
        case <-c:
            fmt.Println("收到中断信号")
            fmt.Println("退出信号处理程序...")
            intr <- true
        case <-quit:
            fmt.Println("退出")
            return
        }
    }
}(c, quit)

在这里,使用了quit通道来请求go协程干净地退出。

英文:

signal.Notify sends signal information to the channel specified, every time process receives signal. But in your code go routine completes, after first signal. So, It is not able to trap the signal again.

One simple way to do this is, you need to have a infinite loop with select clause in the go routine.

go func(c chan os.Signal, quit chan bool) {
	for {
		select {
		case &lt;-c:
			fmt.Println(&quot;Got interrupt signal&quot;)
			fmt.Println(&quot;Exiting signal handler..&quot;)
			intr &lt;- true
		case &lt;-quit:
			fmt.Println(&quot;quit&quot;)
			return
		}
	}

}(c, quit)

Here, Channel quit is used, to request go-routine to exit cleanly.

huangapple
  • 本文由 发表于 2016年4月14日 00:18:07
  • 转载请务必保留本文链接:https://go.coder-hub.com/36604177.html
匿名

发表评论

匿名网友

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

确定