ImageMagick can run on Windows PowerShell but can't run with go

Issue

I need to add watermark with ImageMagick, for some reason, I need to run it with golang.

Here is my code snippet

package main

import (
    "fmt"
    "os"
    "os/exec"
    "path/filepath"
    "runtime"
    "strings"
)

func main() {
    currentDir, _ := os.Getwd()

    sourceImg := os.Args[1]

    sourceName := filepath.Base(sourceImg)
    sourceExt := filepath.Ext(sourceImg)
    imgNameWithoutExt := strings.Replace(sourceName, sourceExt, "", 1)
    targetImgName := imgNameWithoutExt + "_wm" + sourceExt
    targetImg := filepath.Join(filepath.Dir(sourceImg), targetImgName)

    command := "bash"
    secondParam := "-c"
    // In macOS or Linux, use backslash to escape parenthesis
    cmdStr := `magick "` + sourceImg + `" -set option:watermarkWidth "%[fx:int(w*0.25)]" -alpha set -background none \\( -fill "#FFFFFF80" -stroke "#FF000080" -strokeWidth 3 -undercolor "#FF000080" -font "arial.ttf" -size "%[watermarkWidth]x" label:"This is watermark" -gravity center -geometry +10+10 -rotate -30 \\) -composite -quality 40 "` + targetImg + `"`

    if runtime.GOOS == "windows" {
        sourceImg = strings.ReplaceAll(sourceImg, "\\", "\\\\")
        targetImg = strings.ReplaceAll(targetImg, "\\", "\\\\")
        // In PowerShell, use babckstick (`) to escape parenthesis
        command = "cmd"
        secondParam = "/c"
        cmdStr = `magick "` + sourceImg + `" -set option:watermarkWidth "%[fx:int(w*0.25)]" -alpha set -background none ` + "`(" + ` -fill "#FFFFFF80" -stroke "#FF000080" -strokeWidth 3 -undercolor "#FF000080" -font "arial.ttf" -size "%[watermarkWidth]x" label:"This is watermark" -gravity center -geometry +10+10 -rotate -30 ` + "`)" + ` -composite -quality 40 "` + targetImg + `"`
    }

    fmt.Println(cmdStr)

    cmd := exec.Command(command, secondParam, cmdStr)
    cmd.Dir = currentDir
    ouput, err := cmd.Output()
    if err != nil {
        fmt.Println("Error:", ouput, err.Error())
    } else {
        fmt.Println("Watermark was successfully added!")
    }
}

Because I’ve use os.Getwd() in the code, so we cannot run it directly through go run main.go, instead, we should build an executable

# build Windows executable
GOOS=windows GOARCH=amd64 go build -ldflags "-w -s" -o "test-magick.exe" main.go

# build macOS executable
GOOS=darwin GOARCH=amd64 go build -ldflags "-w -s" -o "test-magick" main.go

# build Linux executable(I didn't test)
GOOS=linux GOARCH=amd64 go build -ldflags "-w -s" -o "test-magick" main.go

On macOS, the executable works fine, the watermark was successfully added
enter image description here

On Windows(in PowerShell), it returns an error, actually no specified error message, it just failed
enter image description here

Anyone who knows how to solve this error?

Solution

You’re not using PowerShell to invoke your executable from inside your Go application, you’re using cmd.exe, which has different syntax rules (it doesn’t recognize ` (the backtick) as the escape character, % is a metacharacter, no support for escaping " as \", …)

Therefore, because you’re mistakenly passing a command line designed for powershell.exe (the Windows PowerShell CLI) to cmd.exe, the legacy Windows shell, which fails, due to the syntax differences.

Therefore, replace:

    command = "cmd"
    secondParam = "/c"

with:

    command = "powershell.exe"
    secondParam = "-c"

Additionally, consider placing the following arguments before -c, for added robustness:

   "-ExecutionPolicy", "Bypass", "-NoProfile"

See the documentation for powershell.exe.


Taking a step back:

Your executable call doesn’t use any shell features (such as redirecting to a file, connecting multiple commands via a pipeline, …), so you could simply invoke magic directly, with it and all arguments passed individually to exec.Command(), which speeds up the operation and avoids the need for escaping.

Answered By – mklement0

Answer Checked By – Candace Johnson (GoLangFix Volunteer)

Leave a Reply

Your email address will not be published.