ImageMagick可以在Windows PowerShell上运行,但无法与go一起运行。

huangapple go评论73阅读模式
英文:

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

问题

我需要使用ImageMagick添加水印,由于某些原因,我需要使用golang来运行它。

这是我的代码片段:

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"
	// 在 macOS 或 Linux 中,使用反斜杠来转义括号
	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, "\\", "\\\\")
		// 在 PowerShell 中,使用反引号(`)来转义括号
		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!")
	}
}

由于代码中使用了os.Getwd(),所以我们不能直接通过go run main.go来运行它,而是需要构建一个可执行文件:

# 构建 Windows 可执行文件
GOOS=windows GOARCH=amd64 go build -ldflags "-w -s" -o "test-magick.exe" main.go

# 构建 macOS 可执行文件
GOOS=darwin GOARCH=amd64 go build -ldflags "-w -s" -o "test-magick" main.go

# 构建 Linux 可执行文件(我没有测试)
GOOS=linux GOARCH=amd64 go build -ldflags "-w -s" -o "test-magick" main.go

在 macOS 上,可执行文件可以正常工作,成功添加了水印。

在 Windows 上(PowerShell中),它返回一个错误,实际上没有指定的错误消息,只是失败了。

有谁知道如何解决这个错误?

英文:

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
ImageMagick可以在Windows PowerShell上运行,但无法与go一起运行。

On Windows(in PowerShell), it returns an error, actually no specified error message, it just failed
ImageMagick可以在Windows PowerShell上运行,但无法与go一起运行。

Anyone who knows how to solve this error?

答案1

得分: 2

你在你的Go应用程序中并没有使用PowerShell来调用你的可执行文件,而是使用了cmd.exe,它有不同的语法规则(它不将`(反引号)识别为转义字符,%是一个元字符,不支持将"转义为\",...)

因此,因为你错误地将一个为powershell.exe(Windows PowerShell CLI)设计的命令行传递给了cmd.exe,这是一个语法不同的旧版Windows shell,所以会失败。

所以,将以下代码替换为:

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

此外,考虑在-c之前添加以下参数,以增加鲁棒性:

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

请参阅powershell.exe的文档


退一步说:

你的可执行文件调用没有使用任何shell特性(比如重定向到文件,通过管道连接多个命令等),所以你可以直接使用exec.Command()magick及其所有参数作为单独的参数传递给它,这样可以加快操作速度并避免转义的需要。

英文:

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 magick directly, with it and all its arguments passed individually to exec.Command(), which speeds up the operation and avoids the need for escaping.

huangapple
  • 本文由 发表于 2022年4月4日 20:04:47
  • 转载请务必保留本文链接:https://go.coder-hub.com/71736969.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定