読者です 読者をやめる 読者になる 読者になる

Go for Windows で子プロセスに二重引用符を引数でそのまま渡したい時

問題の症状

親プロセスのソース:

// exec1.go
package main

import (
    "os"
    "os/exec"
)

func main() {
    c := exec.Command("foo", `"<BAR>"`)
    c.Stdout = os.Stdout
    c.Stderr = os.Stderr
    c.Stdin = os.Stdin
    c.Run()
}

子プロセスのソース(バッチファイル):

@rem foo.cmd
@echo %0 %*
@exit /b 0

実行すると

$ go run exec1.go
foo \"<BAR>\"

二重引用符の前にバックスラッシュがついてしまう。

解決方法

バックスラッシュが付かないようにするには、Go言語標準のexec.Cmd.Args[]Windows 形式の引数形式に展開する処理をパスして、自前で直接指定すればよい。

//exec2.go
package main

import (
    "os"
    "os/exec"
    "syscall"
)

func main() {
    c := exec.Command("foo")
    c.SysProcAttr = &syscall.SysProcAttr{CmdLine: `foo "<BAR>"`}
    c.Stdout = os.Stdout
    c.Stderr = os.Stderr
    c.Stdin = os.Stdin
    c.Run()
}
$ go run exec2.go
foo "<BAR>"

なぜ、こんなことをする必要があるのか

FIND.EXE や CMD.EXE は、引数につけられた二重引用符の有無で、その引数がどういうものか判別しています。このため、Goのプログラムから FIND.EXE が意図どおりに呼べないという問題が発生していました。

こういう仕様って、どこに書いてあったの?

ここ