英文:
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 <-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 ("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("Got signal", s)
fmt.Println("Exiting signal handler..")
intr <- true
return
}()
breakOutOfHere:
for {
select {
case <-intr:
fmt.Println("Interrupted.. Continuing")
continue
default:
fmt.Printf("Enter text: ")
r := bufio.NewReader(os.Stdin)
text, ret := r.ReadString('\n')
if ret == io.EOF {
fmt.Println("Invalid input1")
continue
} else if ret == nil {
text = strings.TrimSpace(text)
if text == "" {
fmt.Println("Invalid input2")
continue
} else {
fmt.Printf("Entered:%s\n", 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 <-c:
fmt.Println("Got interrupt signal")
fmt.Println("Exiting signal handler..")
intr <- true
case <-quit:
fmt.Println("quit")
return
}
}
}(c, quit)
Here, Channel quit
is used, to request go-routine to exit cleanly.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论