How to stop Golang from replacing double-quotes with backslashes when executing a Windows command? - windows

I am writing a program in Golang that will use Mozilla's Thunderbird email client to send email. The Windows command that should be executed is:
start "" "C:\Program Files (x86)\Mozilla Thunderbird\thunderbird.exe" -compose "to='CloudCoin#Protonmail.com',subject='Subject1',body='Hello'" -offline
My Go code looks like this (command is the one listed above) :
var command string
command = `start "" "C:\Program Files (x86)\Mozilla Thunderbird\thunderbird.exe"`
command += ` -compose "to='` + toAddress + `',`
command += `subject='Subject1',`
command += `body='Hello'"`
command += ` -offline`
cmd := exec.Command("cmd.exe", "/C", command)
But I get an error:
Windows cannot find '\\'. Make sure you typed the name correctly, and then try again.
If I change the code to this (moving the word start):
var command string
command = ` "" "C:\Program Files (x86)\Mozilla Thunderbird\thunderbird.exe"`
command += ` -compose "to='` + toAddress + `',`
command += `subject='Subject1',`
command += `body='Hello'"`
command += ` -offline`
fmt.Println("Command: " + command)
cmd := exec.Command("cmd.exe", "/C", "start", command)
Then I get another error:
Windows cannot find 'Files'. Make sure you typed the name correctly, and then try again.
It seems that instead of trying to start "" it is trying to start \\. How can I keep my double-quotes?

Your problem likely is that every separate string passed to exec.Command is passed on (without parsing it) as a single argument to cmd.exe, which does probably not split given strings either, so you have to do it yourself.
See this example where the argument is being split up too. You should be able to leave " out since you split it up manually anyway, or write a program for it or run it with an interpreter which does the splitting.
func do() {
args := []string{
"/C",
"start",
"",
`C:\Program Files (x86)\Mozilla Thunderbird\thunderbird.exe`,
"-compose",
"to=" + toAddress + ",subject=Subject1,body=Hello",
"-offline",
}
cmd := exec.Command("cmd.exe", args...)
}

Related

Creating a folder without space is failing

I have a pure QML application where I'm trying to create a folder with a QProcess on Windows.
If the path I'd like to create contains a space it is working.
If the path I'd like to create DOES NOT contain a space it is NOT working.
QML function:
function mkdir(qproc, path) {
var cmd;
var res = false;
// Platform-based command
switch (Qt.platform.os) {
case "windows":
cmd = "cmd /s /c mkdir \"" + path + "\"";
break;
default:
cmd = "/bin/sh -c \"mkdir -p '"+path+"'\"";
}
qproc.start(cmd);
res = qproc.waitForFinished(5000);
}
Which ends up as DOS commands:
Path with space: cmd /s /c mkdir "D:/Misc/temp/A Folder/" ==> A Folder is created
Path without space: cmd /s /c mkdir "D:/Misc/temp/AFolder/" ==> AFolder is NOT created
Two remarks:
the \s flag seems having no effect, neither positive nor negative (I could remove it)
launched individually in a cmd-prompt, the 2 calls are working. The issue seems to be in the QML<->cmd interaction.

unable to use git echo commands in golang

I'm unable to echo a string into a new file , it works if I use a filename with some extension(.txt, .go, etc) but it doesn't work when I just use a filename without file extension
I want the below bash commands to be executed in golang
echo "testDir/*"> .git/info/sparse-checkout
git checkout <Branch Name>
code snippet:
// Remove the redirect from command
cmd := exec.Command("echo", "testDir/*")
// Make test file
testFile, err := os.Create(".git/info/sparse-checkout")
if err != nil {
panic(err)
}
defer testFile.Close()
// Redirect the output here (this is the key part)
cmd.Stdout = testFile
err = cmd.Start(); if err != nil {
panic(err)
}
cmd.Wait()
branchCmd := exec.Command("git checkout <Branch Name>")
The echo command just print the list of arguments. The command interpreter (shell) expand the * based on globing existing filenames.
Without the shell, you can’t echo anything. You may need to call the shell (bash for instance) to expand and echo it, or use the io package to list files based on this pattern
If I understand correctly : in your bash command, echo "testDir/*" doesn't expand anything, it just outputs the string "testDir/*". echo ... > file is just one of the gazillion ways to set the file content to a given value from your shell.
If you want to write a fixed string to a file in go, just write it :
_, err := testFile.Write([]byte("testDir/*\n"))
no need to start some external process to echo a value on stdout.

Can't execute ffprobe command

I try to get video file duration with ffprobe. When I run this code I get error:
exit status 1:
var out bytes.Buffer
var stderr bytes.Buffer
cmdArgs := []string{"-i", "bunny.mp4", "-show_entries", "format=duration", "-v", "quiet", "-of", `csv="p=0"`}
cmd := exec.Command("ffprobe", cmdArguments...)
cmd.Stdout = &out
cmd.Stderr = &stderr
if err := cmd.Run(); err != nil {
fmt.Println(fmt.Sprint(err) + ": " + stderr.String())
}
fmt.Printf("command output: %q\n", out.String())
But when I pass argruments without -of csv="p=0" like this:
cmdArgs := []string{"-i", "bunny.mp4", "-show_entries", "format=duration", "-v", "quiet"}
It works and returns the result (but in bad format):
command output: "[FORMAT]\nduration=3.008000\n[/FORMAT]\n"
So what is the problem and how to solve it ?
Try formatting the argument like this (use double quotes for the string instead of backticks and remove the inner double quotes):
cmdArgs := []string{..., "csv=p=0"}
The Go exec package does not invoke the sytem shell to process the arguments, so you do not need to take the same precautions when specifying them. In this case, there's no need to wrap the portion after the first "=" in quotes.
From the package documentation:
Unlike the "system" library call from C and other languages, the
os/exec package intentionally does not invoke the system shell and
does not expand any glob patterns or handle other expansions,
pipelines, or redirections typically done by shells. The package
behaves more like C's "exec" family of functions. To expand glob
patterns, either call the shell directly, taking care to escape any
dangerous input, or use the path/filepath package's Glob function. To
expand environment variables, use package os's ExpandEnv.

Running a command from a string with parameters in go

I am trying to run a command with go. The command is in a string.
package main
import (
"log"
"os"
"os/exec"
"strings"
"github.com/davecgh/go-spew/spew"
)
func main() {
commandToRun := `echo $HOME`
log.Printf("Running %s\n", commandToRun)
args := strings.Fields(commandToRun)
spew.Dump(args[1:len(args)])
command := exec.Command(args[0], args[1:len(args)]...)
command.Stdout = os.Stdout
command.Stdin = os.Stdin
command.Stderr = os.Stderr
err := command.Run()
if err != nil {
log.Printf("Command finished with error: %v", err)
}
}
The output is:
2018/11/14 09:41:22 Running echo $HOME
([]string) (len=1 cap=1) {
(string) (len=5) "$HOME"
}
$HOME
What I'd like to have is:
2018/11/14 09:41:22 Running echo $HOME
([]string) (len=1 cap=1) {
(string) (len=5) "$HOME"
}
/home/whatever
Looks like go is sanitizing the string somehow. So the $HOME is not expanded. Is there any way of running the string exactly as if it was typed into the shell?
This is the important part. Ideally I'd like to turn from string to type in the current shell.
EDIT: The example below solve the simplest scenario but doesn't cover the "running the string exactly as if it was typed into the shell" part.
If I switch to expandenv:
commandToRun := os.ExpandEnv(`echo "$HOME"`)
I get:
2018/11/14 11:45:44 Running echo "/Users/rafael"
([]string) (len=1 cap=1) {
(string) (len=15) "\"/home/whatever\""
}
"/home/whatever"
What I'd get in the shell is:
$ > echo "$HOME"
/home/whatever
without the quotes.
This is close to what I want but not exactly it.
$HOME (and all other env variables) are expanded by the shell. You're not executing a shell, so they don't get expanded.
You need to look up the env variable directly in go, with something like:
command := exec.Command("echo", os.Getenv("HOME"))
or this:
commandToRun := os.ExpandEnv("echo $HOME")
args := strings.Fields(commandToRun)
command := exec.Command(args[0], args[1:]...)
Note that this last approach won't work if $HOME expands to a string containing whitespace, so the os.Getenv method is generally safer/preferred for this use case.
Before executing the command, you can actively expand all env vars in the string using os.ExpandEnv:
os.ExpandEnv("echo $HOME")
From the docs:
ExpandEnv replaces ${var} or $var in the string according to the values of the current environment variables. References to undefined variables are replaced by the empty string.
If you want to get the output of $ echo $HOME, the minimal code you need is
fmt.Println(os.Getenv("HOME"))
Nothing more is needed.
If you use os.ExpandEnv("echo $HOME"), then first $HOME var will be expanded and then it will give you a string like echo /home/<user>
If you use command := exec.Command("echo", os.Getenv("HOME")), then it will be resolved as command := exec.Command("echo", "/home/<user>") and finally which will give output /home/<user>
If you use
commandToRun := os.ExpandEnv("echo $HOME")
command := exec.Command(strings.Fields(commandToRun)...)
then it will be process like previous cases.
So better way is using only fmt.Println(os.Getenv("HOME")).

How can i use system() with rxrepl in WinCC OA?

I try to use:
string result;
string path = "C:/winccoa.projects/filters/bin/tools/rxrepl.exe";
string cmd = "'opcki' | " + path + " -s 'op' -r 'tata'";
system(cmd, result);
DebugN(result);
But in LogViewer i see nothing, instead ["tatacki"]
Why? What i doing wrong?
In PowerShell that works fine:
PS C:\> 'opcki' | C:/winccoa.projects/filters/bin/tools/rxrepl.exe -s "op" -r "tata"
tatacki
I'm assuming that WinCC's system() function targets cmd.exe, not powershell.exe (which is typical, because historically cmd.exe has been the default shell, and APIs are unlikely to change, so as to maintain backward compatibility).
Therefore, formulate your command for cmd.exe:
string cmd = "echo opcki | " + path + " -s op -r tata";
Not the use of echo to produce output and the omission of single-quoting ('...'), which cmd.exe doesn't recognize.
If embedded quoting were needed, you'd have to use `" inside "..." PowerShell strings (or use '...' PowerShell strings (whose content is taken literally) and embed " chars. as-is).

Resources