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
On Windows(in PowerShell), it returns an error, actually no specified error message, it just failed
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.