Home > front end >  Go : Correctly running external program with arguments
Go : Correctly running external program with arguments

Time:06-30

Good evening, I'm working on converting some tools written in python to Go in order to better understand it.

I need the program to call an external .exe with some arguments in order for it to correctly format some data. In the windows shell I can do C:\path_to_exe\file.exe arg1 arg2 "C:\path_to_output\output.txt"

I believe the correct method to do this in Go would be using exec.Command, but I'm not getting any...meaningful results.

out, err := exec.Command("cmd", "C:\\path\\tools\\util\\Utility.exe C:\\file_Location \"select * from TABLE\" C:\\output_path\\output.txt").Output()

            fmt.Printf("\n", string(out))

            if err != nil {
                println(" Error running decomp ", err)
            }

This appears to be running command, as the output I am receiving is:

%!(EXTRA string=Microsoft Windows [Version 10.0.22000.739]
(c) Microsoft Corporation. All rights reserved.
Process finished with the exit code 0

Just for giggles I tried breaking up the arguments, but the same result was achieved

out, err := exec.Command("cmd", exPath, utilPath, statement, textOutputPath "test.txt").Output()

I'm expecting the executed program to run, parse the correct file based on the input, and output the specified txt file. I am left with no .txt file, and the go program runs much faster then the parsing should take.

There must be something I'm missing, could someone please provide some insight on the correct usage of exec.Command, because every example I can find appears to show that this should work.

CodePudding user response:

Why are you spawning cmd.exe and having it run your utility.exe?

You can just spawn utility on its own.

For instance, suppose you have two binaries, hello and say-hello living in the same directory, compiled from

  • hello.gohello:

    package main
    
    import (
      "fmt"
      "os"
    )
    
    func main() {
    
      argv := os.Args[1:]
      if len(argv) == 0 {
        argv = []string{"world"}
      }
    
      for _, arg := range argv {
        fmt.Printf("Hello, %s!\n", arg)
      }
    
    }
    
  • say-hello.gosay-hello:

    package main
    
    import (
      "fmt"
      "os"
      "os/exec"
    )
    
    func main() {
      process := exec.Command("./hello", os.Args[1:]...)
      process.Stdin = os.Stdin
      process.Stdout = os.Stdout
      process.Stderr = os.Stderr
    
      if err := process.Run(); err != nil {
        fmt.Printf("Command failed with exit code %d\n", process.ProcessState.ExitCode())
        fmt.Println(err)
      }
    }
    

You can then run the command:

$ ./say-hello Arawn Gywdion Sarah Hannah

And get back the expected

Hello, Arawn!
Hello, Gwydion!
Hello, Sarah!
Hello, Hannah!

CodePudding user response:

It appears to be working correctly according to the outputs in your question.

A few suggestions:

  • It might be useful to print the command out as a string before running it, to check it's what you want.
  • You may find backticks useful when you have a string containing backslashes and quotation marks.
  • You have not supplied any format to fmt.Printf, hence the EXTRA in that output.
  • Using println to print the error will not stringify it, so use fmt.Printf for that too.
package main

import (
    "fmt"
    "os/exec"
)

func main() {
    cmd := exec.Command("cmd", `C:\path\tools\util\Utility.exe C:\file_Location "select * from TABLE" C:\output_path\output.txt`)
    fmt.Printf("%s\n", cmd.String())
    out, err := cmd.Output()
    fmt.Printf("%s\n", string(out))
    if err != nil {
        fmt.Printf(" Error running decomp %s\n", err)
    }
}

Playground: https://go.dev/play/p/3t0aOxAZRtU

  • Related