Search code examples
gocmd

Golang execute commands with double quotes


I made a simple program which takes user input and then executes it as a system command.

This takes user input and stores it in command variable:

fmt.Print(">  ")
    
scanner := bufio.NewScanner(os.Stdin)
scanner.Scan()
command := scanner.Text()

Then I execute it and print out Stdout aswell as Stderr:

cmd := exec.Command("cmd", "/c", command)

cmd.Stdout = customOutput{}
cmd.Stderr = os.Stderr

if err := cmd.Run(); err != nil {
    fmt.Println("could not run command: ", err)
}

Now when I execute command like: ipconfig, arp -a, ping, it all works great and the output is getting printed out as it executes, but when I try to use double quotes in the command it just breaks.

The problem: I tried doing: echo hello world > file.txt and this works fine but as soon as I put the filename in quotes: echo hello world > "file.txt" I'm getting exit status 1 and from Stderr I'm getting: The filename, directory name, or volume label syntax is incorrect.

I tried:

  • searching, even asking chat-gpt but I can't figure out why this happens.
  • replacing the quotes before executing the command to: " or ^" but nothing works.

I'm using windows 10 btw.

Full code for better understanding:

package main

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

type customOutput struct{}

func (c customOutput) Write(p []byte) (int, error) {
    fmt.Println("received output: ", string(p))
    return len(p), nil
}

func main() {
    fmt.Print(">  ")
    
    scanner := bufio.NewScanner(os.Stdin)
    scanner.Scan()
    command := scanner.Text()

    cmd := exec.Command("cmd", "/c", command)

    cmd.Stdout = customOutput{}
    cmd.Stderr = os.Stderr

    if err := cmd.Run(); err != nil {
        fmt.Println("could not run command: ", err)
    }
}

Solution

  • You need to do

    cmd := exec.Command("cmd.exe")
    cmd.SysProcAttr = &syscall.SysProcAttr{CmdLine: fmt.Sprintf(`/c "%s"`, command)}
    cmd.Stdout = customOutput{}
    cmd.Stderr = os.Stderr
    
    if err := cmd.Run(); err != nil {
        fmt.Println("could not run command: ", err)
    }
    

    and then probably escape the quotes in command.

    https://pkg.go.dev/os/exec#Command

    On Windows, processes receive the whole command line as a single string and do their own parsing. Command combines and quotes Args into a command line string with an algorithm compatible with applications using CommandLineToArgvW (which is the most common way). *Notable exceptions are msiexec.exe and cmd.exe (and thus, all batch files), which have a different unquoting algorithm. In these or other similar cases, you can do the quoting yourself and provide the full command line in SysProcAttr.CmdLine, leaving Args empty.