10

I want to execute find Windows command using exec package, but windows is doing some weird escaping.

I have something like: out, err := exec.Command("find", `"SomeText"`).Output()

but this is throwing error because Windows is converting this to find /SomeText"

Does anyone know why? How I can execute find on windows using exec package?

Thanks!

user232343
  • 2,008
  • 5
  • 22
  • 34
  • Not Windows, this must be something Go's `.Command()` function is doing. The first thing to try is putting the entire command in a single string rather than having it split into parts. – Harry Johnston Mar 10 '15 at 01:34
  • @HarryJohnston perhaps look at [os/exec](https://golang.org/pkg/os/exec) docs first and note that it provides no function/method that takes an entire command but only pre-separated arguments (as is sane) before suggesting someone do that. – Dave C Mar 10 '15 at 02:21
  • @DaveC: doesn't the ... in the argument list mean zero or more? (Also, on Windows, using pre-separated arguments *isn't* sane. The underlying function that starts a new process takes a single string for the command line, not an array like UNIX. So a function that takes separated arguments has to figure out how to put them together, and as often as not gets it wrong; that's probably what happened in the OPs case.) – Harry Johnston Mar 10 '15 at 02:30
  • @HarryJohnston yes the call is legal, but calling `exec.Command("foo bar baz")` just tries to find an executable called "foo bar baz" it does **not** try and call "foo" with arguments "bar" and "baz". If you want that then you need to split out the arguments yourself which as proliferation of various shells with slightly different syntax indicates, is not straight forward and doesn't have a one-size-fits-all solution. – Dave C Mar 10 '15 at 02:42
  • @DaveC: I'll take your word for it, but it isn't obviously true from the documentation. In the Windows case there's no shell involved - or, rather, even if os/exec invokes the shell, that doesn't affect how command line arguments are parsed. – Harry Johnston Mar 10 '15 at 02:48
  • I am running go 1.4. When I executed above command I got panic, and from panic message I saw that "find /SomeText"" was executed and failing. – user232343 Mar 10 '15 at 08:51

2 Answers2

7

OK, it's a bit more complicated than you might have expected, but there is a solution:

package main

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

func main() {
    cmd := exec.Command(`find`)
    cmd.SysProcAttr = &syscall.SysProcAttr{}
    cmd.SysProcAttr.CmdLine = `find "SomeText" test.txt`
    out, err := cmd.Output()
    fmt.Printf("%s\n", out)
    fmt.Printf("%v\n", err)
}

Unfortunately, although support for this was added in 2011, it doesn't appear to have made it into the documentation yet. (Although perhaps I just don't know where to look.)

Harry Johnston
  • 35,639
  • 6
  • 68
  • 158
  • 1
    This does not appear to work in current versions of Go (1.11.1), as it returns `cmd.SysProcAttr.CmdLine undefined (type *syscall.SysProcAttr has no field or method CmdLine)` – DarkerIvy Oct 15 '18 at 20:25
  • @DarkerIvy, [the functionality appears to still be in the source code](https://golang.org/src/syscall/exec_windows.go), although the [syscall package is deprecated](https://docs.google.com/document/d/1QXzI9I1pOfZPujQzxhyRy6EeHYTQitKKjHfpq0zpxZs/edit). Presumably the compatibility rules mentioned in the latter link mean this code should still work, if it no longer does perhaps that should be filed as a bug? (You're sure you're running the Windows version of Go and not, e.g., Cygwin or something?) – Harry Johnston Oct 15 '18 at 22:28
0

FYI, running:

package main

import (
    "fmt"
    "os/exec"
)

func main() {
    cmd := exec.Command("find", `"SomeText"`)
    fmt.Printf("Path: %q, args[1]: %q\n", cmd.Path, cmd.Args[1])
}

playground

On unix gives:

Path: "/usr/bin/find", args[1]: "\"SomeText\""

And cross compiled to Windows and run on Win7 gives:

Path: "C:\\Windows\\system32\\find.exe", args[1]: "\"SomeText\""

Both look correct to me.

Adding out, err := cmd.Output() to the Windows cross-compile gives the following for fmt.Printf("%#v\%v\n", err, err):

&exec.ExitError{ProcessState:(*os.ProcessState)(0xc0820046a0)}
exit status 1

But I imagine that's just because find fails to find anything.

Dave C
  • 7,729
  • 4
  • 49
  • 65
  • Further experiments with adding an extra "file.txt" argument to the command and making that include SomeText indicate that it returns that same "error" (exit status 1) but also produces the matched filename as output. (Again, cross-compiled to and run on a Win7 virtual machine). – Dave C Mar 10 '15 at 02:56
  • I've now tried this myself and it is not working as expected. The "find" command produces the filename as output (because it always does) but does not find the string. Further investigation demonstrated that this is because the command line being run says `find \"SomeText\" test.txt` instead of `find "SomeText" test.txt` ; any idea how to correct that? – Harry Johnston Mar 10 '15 at 19:39
  • The prints quoted above have `\"` since I used %q formatting which adds quotes and escapes existing quotes (I find that makes it easier to spot things like `"foo "` and the like). I copied the question's use of backticks which produces a string that includes the quotes. If that isn't desired/required then one of the set of quotes around SomeText should be removed. – Dave C Mar 10 '15 at 20:26
  • That's what I figured originally, but by substituting `find.exe` with a utility that just pops up a message box showing the command line, I discovered that the backslashes really were winding up in the command line being executed. If I don't include both sets of quote marks, then the command line doesn't have any quote marks: `find SomeText test.txt` and that won't work either, because the quote marks are mandatory. (Perhaps this is a bug in Go?) – Harry Johnston Mar 10 '15 at 20:37
  • 1
    As it turns out, the issue has already been addressed, the fix just hasn't made it into the documentation yet. https://github.com/golang/go/issues/1849 – Harry Johnston Mar 11 '15 at 22:13