shellgogofmt

"rune literal not terminated" error when attempting to run gofmt -r with exec.Command


In the following directory structure,

.
├── foo.go
├── go.mod
└── main.go

I have a foo.go with a simple type definition:

package main

type Foo struct {
    Baz string
}

If I run gofmt -r from the command line to replace a variable name, it works:

> gofmt -r 'Foo -> Bar' foo.go
package main

type Bar struct {
    Baz string
}

However, if I try to do this from main.go with the program

package main

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

func main() {
    combinedOutput, err := exec.Command("gofmt", "-r", "'Foo -> Bar'", "foo.go").CombinedOutput()
    if err != nil {
        log.Fatalf("gofmt foo.go: %v. Combined output: %s", err, combinedOutput)
    }
    fmt.Println(string(combinedOutput))
}

I get an error:

> go run main.go
2023/01/14 23:42:07 gofmt foo.go: exit status 2. Combined output: parsing pattern 'Foo  at 1:1: rune literal not terminated
exit status 1

Any idea what is causing this?


Solution

  • You don't need to quote arguments to exec.Command; quoting is a feature of your shell, and doesn't apply when you make system calls. It's also not necessary, because quoting is done to delineate arguments in the shell, but in exec.Command, the arguments are separated as arguments to the function call.

    Concretely:

    exec.Command("gofmt", "-r", "'Foo -> Bar'", "foo.go")
    

    should be

    exec.Command("gofmt", "-r", "Foo -> Bar", "foo.go")