In order to learn Go, I've implemented the cat
program in Go. I want to know if the code is idiomatic and what could be improved.
package main
import "fmt"
import "io"
import "os"
var errors int
func main() {
catFilesOrStdin(os.Args[1:])
os.Exit(getExitValue())
}
func getExitValue() int {
if (hadErrors()) {
return 1
} else {
return 0
}
}
func hadErrors() bool {
return errors > 0
}
func catFilesOrStdin(files []string) {
if len(files) > 0 {
catFiles(files)
} else {
catStream(os.Stdin)
}
}
func catFiles(files []string) {
for _,file := range files {
catFile(file)
}
}
func catFile(file string) {
stream, err := os.Open(file)
if err != nil {
fmt.Fprintln(os.Stderr, err)
errors++
return
}
catStream(stream)
defer stream.Close()
}
func catStream(stream *os.File) {
_, err := io.Copy(os.Stdout, stream)
if err != nil {
fmt.Fprintln(os.Stderr, err)
errors++
}
}
1 Answer 1
I'd put the defer
statement in catFile above the call to catStream, not after.
You might want to consider log.Fatal
if the os.Open
fails.
If you're importing multiple packages, the standard is a single import statement with all the packages in parentheses.
The global errors
variable technically should be protected with a sync.Mutex
, but more importantly, log.Fatal
on any error instead.
-
1\$\begingroup\$ wrt
log.Fatal
on any error, although I'd probably do that if implementing something like this from scratch, that is not the behaviour of unixcat
. E.g.date | cat /nosuchfile -
gives a warning message and continues on (but exits with 1 instead of 0); this is the behaviour being implemented here. \$\endgroup\$Dave C– Dave C2015年08月13日 14:26:17 +00:00Commented Aug 13, 2015 at 14:26 -
2\$\begingroup\$ Although, I would use the
log
package. Something likelog.SetPrefix("cat: "); log.SetFlags(0)
thenlog.Println(err)
rather than explicitlyfmt.Fprintln
toos.Stderr
. But that's more of a preference thing. You could probably wrap warning logging into a function that also diderrors++
. \$\endgroup\$Dave C– Dave C2015年08月13日 14:29:50 +00:00Commented Aug 13, 2015 at 14:29
func catStream(r io.Reader)
rather than require a*os.File
. \$\endgroup\$io.Reader
instead ofos.File
, I agree. \$\endgroup\$