学习吧,试图找出exec包。 我可以通过哪些方式改进代码?

Wrote a simple program that calls "ls", then passes each line through regexp filtering for files that end in an "s". ls is used only for the purposes of learning the exec package. How can I improve the code below to be more correct/succinct/go-ish?

package main

import (
    "bufio"
    "fmt"
    "os/exec"
    "regexp"
)

func main() {

    cmd := exec.Command("ls")
    stdout, _ := cmd.StdoutPipe()
    s := bufio.NewReader(stdout)
    cmd.Start()

    go cmd.Wait()

    for {
        l, _, err := s.ReadLine()
        if err != nil {
            break
        }

        if m, err := regexp.Match(".*s$", l); m && err == nil {
            fmt.Println(string(l))
        }
    }
}

The Cmd.Output example in the standard documentation is pretty succinct. It doesn't do any text processing, but it shows how to execute a command and get the output with a single function call.

Here's a way to combine that example with yours,

package main

import (
    "bytes"
    "fmt"
    "log"
    "os/exec"
    "regexp"
)

func main() {
    out, err := exec.Command("ls").Output()
    if err != nil {
        log.Fatal(err)
    }
    fmt.Print(string(bytes.Join(regexp.MustCompile(".*s
").FindAll(out, -1), nil)))
}

If the goal is to get a broad overview of the package, experiment with a number of the functions and methods to learn their different capabilities. Experiment with command arguments. Experiment with the different fields of the Cmd struct. Try not to get too distracted with other packages like regexp, just look for the simplest examples that exercise a package feature.

Of course if you see how you might use an exec feature in a real application, try it. You'll learn that one feature in more depth.

For example,

package main

import (
    "bufio"
    "errors"
    "fmt"
    "io"
    "os/exec"
)

func main() {
    cmd := exec.Command("ls")
    stdout, err := cmd.StdoutPipe()
    if err != nil {
        fmt.Println(err)
        return
    }
    ls := bufio.NewReader(stdout)
    err = cmd.Start()
    if err != nil {
        fmt.Println(err)
        return
    }
    for {
        line, isPrefix, err := ls.ReadLine()
        if isPrefix {
            fmt.Println(errors.New("isPrefix: true"))
            return
        }
        if err != nil {
            if err != io.EOF {
                fmt.Println(err)
                return
            }
            break
        }
        fmt.Println(string(line))
    }
    err = cmd.Wait()
    if err != nil {
        fmt.Println(err)
        return
    }
}

Output (depends on your directory contents):

bench.go
temp.go

Add error checking and stop using regexp for this kind of example.

For any input data larger than the buffer size, ReadLine will inevitably return with isPrefix set to true at some point. ReadString(' ') does what you want - reading until end of line regardless of whether it fits in the buffer, extending the output string as is goes. That also means you could end up with an arbitrarily long string. On the plus side for ReadLine(), it does drop the " " or " " from the end of the line for you, so when using ReadString(), we have to do that ourselves.

package main

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

func main() {
    var err error
    var out io.Reader

    // Create a command object
    cmd := exec.Command("ls")

    // Obtain a pipe to receive the stdout of the command
    out, err = cmd.StdoutPipe(); if err != nil {
        panic(err)
    }

    // Start the child process
    err = cmd.Start(); if err != nil {
        panic(err)
    }

    // bufio.Reader allows us to read all bytes until newline
    s := bufio.NewReader(out)
    for {
        var line string
        line, err = s.ReadString('
')
        if err == io.EOF && len(line) == 0 {
            // Good end of file with no partial line
            break
        }
        if err == io.EOF {
            err := fmt.Errorf("Last line not terminated: %q", line)
            panic(err)
        }
        if err != nil {
            panic(err)
        }
        line = line[:len(line)-1]       // drop the '
'
        if line[len(line)-1] == '' {
            line = line[:len(line)-1]   // drop the ''
        }
        if line[len(line)-1] == 's' {   // Finally check for lines ending in 's'
            fmt.Println(line)
        }
    }

    // Wait for the result of the command; also closes our end of the pipe
    err = cmd.Wait()
    if err != nil {
        panic(err)
    }
}