如何使用Go在命令行上执行diff?

I'm having a problem using Ubuntu 14.04 and executing diff on the command line. Look at the following Go code:

package main

import "fmt"
import "log"
import "os/exec"

func main() {
    output, err := exec.Command("diff", "-u", "/tmp/revision-1", "/tmp/revision-4").Output()
    if err != nil {
        log.Fatalln(err)
    }

    fmt.Println(string(output))
}

If i execute this using go run test.go i get the following error:

2015/03/18 14:39:25 exit status 1
exit status 1

So something is going wrong with diff and it's returning 1 as its exit code. Only the diff command seems to throw an error. If i use the cat or wc command, the code runs fine.

Any ideas why diff doesn't work here but other commands do?

When you run a program with exec, you get an error if the exit code was not 0. From the doc:

The returned error is nil if the command runs, has no problems copying stdin, stdout, and stderr, and exits with a zero exit status.

If the command fails to run or doesn't complete successfully, the error is of type *ExitError. Other error types may be returned for I/O problems.

So what happens here is that diff returns an error when the files are different, yet you treat it like a runtime error. Just change your code to reflect that it isn't. It's possible by checking the error.

e.g. something like this:

    output, err := exec.Command("diff", "-u", "/tmp/revision-1", "/tmp/revision-4").CombinedOutput()
    if err != nil {

        switch err.(type) {
        case *exec.ExitError:
            // this is just an exit code error, no worries
            // do nothing

        default: //couldnt run diff
            log.Fatal(err)
        }
    }

Also, I've changed it get CombinedOutput, so if any diff specific errors occured, you'll see stderr as well.

Note that you'll get a "valid" error even if one of the files doesn't exist. So you can check the ExitError's exit code by doing something like this:

    switch e := err.(type) {
    case *exec.ExitError:

        // we can check the actual error code. This is not platform portable 
        if status, ok := e.Sys().(syscall.WaitStatus); ok {
            // exit code 1 means theres a difference and is not an error
            if status.ExitStatus() != 1 { 
                log.Fatal(err)
            }
        }