2

I'm brand new to Go (as in within the last day) and am playing around with a simple program which processes data from stdin. What I wanted to do was to make it such that if no data were provided to stdin then the program would output a help screen and then exit. The problem I've run into is that the program seems to hang indefinitely when no data is provided via stdin. Here is a short example of the program and my intended usage:

package main

import (
    "fmt"
    "bufio"
    "os"
)


func main() {
    scanner := bufio.NewScanner(os.Stdin)
    scanner.Split(bufio.ScanLines)
    for scanner.Scan() {
        str := scanner.Text()
        fmt.Println(str)
    }
}


Running with input:
go run test.go < lines.txt
line1
line2
line3


Running with no input:
go run test.go

The second case where I do not provide input is what causes the program to hang. Reading through the docs it is not clear to me how I would be able to code the program to not wait for input indefinitely, but rather break if nothing is present on stdin.

2 Answers 2

4

The program is behaving exactly as the code says. The code says to read from stdin. Input to stdin can be provided by redirection (as you show). Or by piping. Or .... Or by user typing to the keyboard. It would be very surprising if in the last case the program would exit before the human can enter something.

A common approach is to do something like (simplified):

var in *os.File
var err error

switch name := flag.Arg(0); {
case name == "":
        in = os.Stdin
default:
        if in, err = os.Open(name); err != nil {
                log.Fatal(err)
        }
}

Ie. allow to process a named file given as the command line argument - but fallback/default to reading stdin when no file name argument is given to the program.

Such approach plays nicely with shell scripting, chaining commands through pipes, etc.

Sign up to request clarification or add additional context in comments.

1 Comment

Makes sense. I was somewhat expecting the answer to be along these lines but I wanted to double check that I wasn't just missing something.
3

It might be a bit of a strech for your usecase especially if you just picked up go, but in general the behaviour you want could be mimiced by using a select with a timeout:

func scanForInput() chan string{
    lines := make(chan string)
    go func(){
       scanner := bufio.NewScanner(os.Stdin)
       scanner.Split(bufio.ScanLines)
       for scanner.Scan() {
           lines <- scanner.Text()
       }
       close(lines)
    }
    return lines
}

func main(){
    lines := scanForInput()
    for {
    select{
         case line, closed := <- lines:
              fmt.Prinln(line)
              if closed {
                  return
              }
         case time.After(1 * time.Second):
              printHelpMessage()
              return
    }
   }
}

Consider it an inspiration for your next learning step.

PS: Wellcome to go, I hope like the language :-)

5 Comments

Thanks for the feedback. Go seems interesting and I certainly appreciate that it has the feel of a scripting language with the speed of a compiled one. I was never quite sold on OO as a particularly efficient (from a code reuse perspective) coding model, so I like the approach they've taken with this language.
Yes. Plus easy concurrency. Plus great tooling. Once you get your head arround the fact that there is no inheritance in go, it's even ok for OO people. You just have to accept the fact, that you have to use a Mixin-style architekture, since there is no dynamic method dispatching.
@flexy: You should remove the outer loop in the go func or insanity will ensue!
The channel should be closed from the sender after the scanner loop is finished.
Good point, modified the code accordingly. Thanks for your feedback :-)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.