dtuct88226 2016-04-13 16:18
浏览 38
已采纳

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

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('
')
            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
", text)
                    break breakOutOfHere
                }
            }
        }
    }
}
  • 写回答

1条回答 默认 最新

  • duananyu9231 2016-04-13 17:27
    关注

    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.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?