Home > Software engineering >  ImageMagick can run on Windows PowerShell but can't run with go
ImageMagick can run on Windows PowerShell but can't run with go

Time:04-05

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?

CodePudding user response:

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.

  • Related