Taking possible EOF as input

1,918 views
Skip to first unread message

Hunter Breathat

unread,
Sep 3, 2018, 12:49:12 PM9/3/18
to golang-nuts
Hey, so I'm currently trying to create a custom shell.

I am currently trying to implement the EOF exit (^D). Currently, I am able to use exit as input and a variety of other
commands, platform-specific; anything, not windows related (WIP), but I am having an issue with the EOF causing
an infinite loop. If you could help that would be splendid.

bufio.Scan() doesn't return io.EOF (So that's a bust even though that's the current implementation)


The section you are looking for is in the file: main.go.

Thanks in advanced
Hunter

PS.
If you are able to even possible walk me through it would help a bit.

peterGo

unread,
Sep 4, 2018, 9:53:43 AM9/4/18
to golang-nuts
Hunter,

Your main.go file is 171 lines of dense code. If you reduced your issue to a small, working piece of code, people would be more likely help you.

You say "bufio.Scan() doesn't return io.EOF." Why?

$ go doc bufio.scanner
type Scanner struct {
}

    Scanning stops unrecoverably at EOF, the first I/O error, or a token too
    large to fit in the buffer.

$ go doc bufio.scanner.scan
func (s *Scanner) Scan() bool

    Scan advances the Scanner to the next token, which will then be available
    through the Bytes or Text method. It returns false when the scan stops,
    either by reaching the end of the input or an error. After Scan returns
    false, the Err method will return any error that occurred during scanning,
    except that if it was io.EOF, Err will return nil.


For example,

package main

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

func main() {
    var input = bufio.NewScanner(os.Stdin)
    for input.Scan() {
        text := input.Text()
        fmt.Println(len(text), text)
    }
    if err := input.Err(); err != nil {
        fmt.Fprintln(os.Stderr, err)
        return
    }
    // input.Scan() == false && input.Err() == nil
    fmt.Println(io.EOF)
}


Output:

# On Linux, Ctrl-D for eof

$ go run eof.go
asd
3 asd
EOF
$


Peter

Hunter Breathat

unread,
Sep 4, 2018, 11:22:54 AM9/4/18
to golang-nuts
EDIT: As peterGo had stated that it is a dense section of code so I'll narrow it down.

var input = bufio.NewScanner(os.Stdin) // Takes user input
...

input.Scan()           //stores user input
command
= input.Text() // Stores user input
switch command {
case " ":
         break
case "":
         if err := input.Err(); err == nil && len(command) == 0 {
                  if err != io.ErrUnexpectedEOF {
                    // EOF support
                      extras.LeaveEOF()
           
                  }
   
         }
}
extras.Leave(command) // Check if command is exit or ^D

// So this is the problem area from what I can tell.

Hunter Breathat

unread,
Sep 4, 2018, 11:28:12 AM9/4/18
to golang-nuts

I only had stated that bufio.Scan() doesn't return io.EOF as the doc stats that it Err returns nil. Which is fine but hard to check for. But from what I've seen the functionality that im looking for would need to check for the SIG.KILL/EOF  on input. Now whether or not   i shoudlgo and alter the bufio.Scanner to return io.EOF as an Err is a different thing. But that would be a smidge excessive for just 1 minor functionality.

Jan Mercl

unread,
Sep 4, 2018, 11:45:01 AM9/4/18
to Hunter Breathat, golang-nuts
On Tue, Sep 4, 2018 at 5:28 PM Hunter Breathat <breatha...@gmail.com> wrote:


> I only had stated that bufio.Scan() doesn't return io.EOF as the doc stats that it Err returns nil. Which is fine but hard to check for. But from what I've seen the functionality that im looking for would need to check for the SIG.KILL/EOF on input. Now whether or not i shoudlgo and alter the bufio.Scanner to return io.EOF as an Err is a different thing. But that would be a smidge excessive for just 1 minor functionality.

.Scan() returns a bool value. io.EOF is not a bool, neither is's a rune ^D. Typical usage of scanner is to loop in `for s.Scan() { do something }`. After the loop one checks s.Err(). If it's nil, then scanner input is exhausted (the underlying readre returned io.EOF). If s.Err() is not nil, something else happened.

The reason that no ^D is ever seen to signal io.EOF while reading from stdin - or any file for that matter - is that it would be an in-band signal, so no files containing that character could be easily processed.

Example:

==== jnml@r550:~/tmp> cat main.go 
package main

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

func main() {
s := bufio.NewScanner(os.Stdin)
for s.Scan() {
fmt.Printf("s.Text() returns %q\n", s.Text())
fmt.Printf("s.Err() returns %v\n", s.Err())
}
fmt.Printf("final s.Text() returns %q\n", s.Text())
fmt.Printf("final s.Err() returns %v\n", s.Err())
}
==== jnml@r550:~/tmp> go build && ./tmp 
after this line we press <enter> and ^D
s.Text() returns "after this line we press <enter> and ^D"
s.Err() returns <nil>
final s.Text() returns ""
final s.Err() returns <nil>
==== jnml@r550:~/tmp>

--

-j

peterGo

unread,
Sep 4, 2018, 12:24:34 PM9/4/18
to golang-nuts
Hunter,

When you write

input.Scan()

you are discarding important information, the return value.

$ go doc bufio.scan

func (s *Scanner) Scan() bool


With the return value, you have an easy test for io.EOF. For example,

scan := input.Scan()
eof := !scan && input.Err() == nil

.
Peter
Reply all
Reply to author
Forward
0 new messages