Hi,
I have a small program to read stdin and execute some commands accordingly.
It s composed of a for loop which waits for data to read on the source.
I found out i could quit the loop by closing its source and catching err on
ReadString calls to skip the iteration.
Qs are,
- is there a cleaner and cheap way to handle reader close ? Or in my case
properly get away from the loop.
- Aside from the previous, How can i use err to detect a closed resource ?
The error i get is "read /dev/stdin: bad file descriptor"
I mean something like described in Dave C last talk. In practical terms,
how to get an identifier for the err, how to compare it.
The specific part of the code looks likes this,
func StdinRead (cmds map[string]func([]string), unhandled *func([]string)) {
reader := bufio.NewReader(os.Stdin)
for {
text, err := reader.ReadString('\n')
if err!=nil {
fmt.Printf("%+v\n", err)
continue
}
text = strings.TrimSpace(text)
args := strings.Split(text, " ")
if len(args)<1 {
continue
}
if fn, ok := cmds[args[0]]; ok {
fn(args[1:])
} else if unhandled!=nil {
h := *unhandled
h(args)
} else {
fmt.Printf("unhandled: '%s' %v\n", args[0], args[1:])
}
}
}
func StdinQuit () {
fmt.Println("Quiting the app, see you!")
os.Stdin.Close() // force close stdin, otherwise the process hangs in the
next for loop
time.Sleep(time.Second * 200)
}
The full one file reproducible example is,
package main
import(
"fmt"
"bufio"
"strings"
"os"
"os/signal"
"syscall"
"time"
)
func main () {
EOApp := Quiter{}
go EOApp.Read()
cmds := make(map[string] func([]string))
cmds["hello"] = func (s []string) {
fmt.Println("hello buddy!")
}
cmds["serve"] = func (s []string) {
// fmt.Println("hello buddy!")
}
cmds["ping"] = func (s []string) {
// fmt.Println("hello buddy!")
}
unhandled := func (s []string) {
if s[0]!="" {
fmt.Printf("nop, i don t know this command: '%s' %v\n", s[0], s[1:])
}
}
EOApp.Register(StdinQuit)
StdinRead(cmds, &unhandled)
}
func StdinRead (cmds map[string]func([]string), unhandled *func([]string)) {
reader := bufio.NewReader(os.Stdin)
for {
text, err := reader.ReadString('\n')
if err!=nil {
fmt.Printf("%+v\n", err)
continue
}
text = strings.TrimSpace(text)
args := strings.Split(text, " ")
if len(args)<1 {
continue
}
if fn, ok := cmds[args[0]]; ok {
fn(args[1:])
} else if unhandled!=nil {
h := *unhandled
h(args)
} else {
fmt.Printf("unhandled: '%s' %v\n", args[0], args[1:])
}
}
}
func StdinQuit () {
fmt.Println("Quiting the app, see you!")
os.Stdin.Close() // force close stdin, otherwise the process hangs in the
next for loop
time.Sleep(time.Second * 200)
}
type Quiter struct {
Components []func()
}
func (q *Quiter) Register (component func()) int {
q.Components = append(q.Components, component)
return len(q.Components)-1
}
func (q *Quiter) exitComponents () {
for _, component := range q.Components {
component()
}
}
func (q *Quiter) Read () {
c := make(chan os.Signal)
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
signalSent := 0
for {
select {
case <-c:
if signalSent <1 {
go func () {
q.exitComponents()
os.Exit(1)
}()
} else {
fmt.Println("Force shutdown")
os.Exit(1)
}
signalSent++
}
}
}
BTW, do i need to make a unbuffered chan of len 2 in Quiter.Read func to
catch both signals appropriately ?
I don t really get the implications of the doc when it says,
Package signal will not block sending to c: the caller must ensure that c
has sufficient buffer space to keep up with the expected signal rate. For a
channel used for notification of just one signal value, a buffer of size 1
is sufficient.
thks
--
You received this message because you are subscribed to the Google Groups
"golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
For more options, visit https://groups.google.com/d/optout.