spin77
spin77

Reputation: 21

file input in golang

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.

Upvotes: 2

Views: 5617

Answers (2)

tike
tike

Reputation: 2294

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 :-)

Upvotes: 3

zzzz
zzzz

Reputation: 91193

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.

Upvotes: 4

Related Questions