diff --git a/os/gproc/gproc_process.go b/os/gproc/gproc_process.go index 0c29fc713cd..9d0193e32ff 100644 --- a/os/gproc/gproc_process.go +++ b/os/gproc/gproc_process.go @@ -12,6 +12,7 @@ import ( "os" "os/exec" "runtime" + "strings" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/propagation" @@ -53,7 +54,14 @@ func NewProcess(path string, args []string, environment ...[]string) *Process { }, } process.Dir, _ = os.Getwd() - process = newProcess(process, args, path) + if len(args) > 0 { + // Exclude of current binary path. + start := 0 + if strings.EqualFold(path, args[0]) { + start = 1 + } + process.Args = append(process.Args, args[start:]...) + } return process } @@ -95,6 +103,11 @@ func (p *Process) Start(ctx context.Context) (int, error) { p.Env = append(p.Env, fmt.Sprintf("%s=%d", envKeyPPid, p.PPid)) p.Env = genv.Filter(p.Env) + // On Windows, this works and doesn't work on other platforms + if runtime.GOOS == "windows" { + joinProcessArgs(p) + } + if err := p.Cmd.Start(); err == nil { if p.Manager != nil { p.Manager.processes.Set(p.Process.Pid, p) diff --git a/os/gproc/gproc_process_newprocess.go b/os/gproc/gproc_process_newprocess.go index f7a1a3d5c07..d424f283fc6 100644 --- a/os/gproc/gproc_process_newprocess.go +++ b/os/gproc/gproc_process_newprocess.go @@ -8,16 +8,5 @@ package gproc -import "strings" - -func newProcess(p *Process, args []string, path string) *Process { - if len(args) > 0 { - // Exclude of current binary path. - start := 0 - if strings.EqualFold(path, args[0]) { - start = 1 - } - p.Args = append(p.Args, args[start:]...) - } - return p -} +// Do nothing, just set it on the Windows platform +func joinProcessArgs(p *Process) {} diff --git a/os/gproc/gproc_process_newprocess_windows.go b/os/gproc/gproc_process_newprocess_windows.go index e9eb8fbac30..704ec6b0b44 100644 --- a/os/gproc/gproc_process_newprocess_windows.go +++ b/os/gproc/gproc_process_newprocess_windows.go @@ -14,10 +14,8 @@ import ( "github.com/gogf/gf/v2/text/gstr" ) -// Because when the underlying parameters are passed in on the Windows platform, -// escape characters will be added, causing some commands to fail. -func newProcess(p *Process, args []string, path string) *Process { +// Set the underlying parameters directly on the Windows platform +func joinProcessArgs(p *Process) { p.SysProcAttr = &syscall.SysProcAttr{} - p.SysProcAttr.CmdLine = path + " " + gstr.Join(args, " ") - return p + p.SysProcAttr.CmdLine = gstr.Join(p.Args, " ") } diff --git a/os/gproc/gproc_z_unit_process_windows_test.go b/os/gproc/gproc_z_unit_process_windows_test.go new file mode 100644 index 00000000000..e1c28b2d261 --- /dev/null +++ b/os/gproc/gproc_z_unit_process_windows_test.go @@ -0,0 +1,78 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +// go test *.go -bench=".*" -benchmem + +//go:build windows + +package gproc_test + +import ( + "path/filepath" + "strings" + "testing" + + "github.com/gogf/gf/v2/os/gctx" + "github.com/gogf/gf/v2/os/gfile" + "github.com/gogf/gf/v2/os/gproc" + "github.com/gogf/gf/v2/test/gtest" +) + +func Test_ProcessRun(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + binary := gproc.SearchBinary("go") + t.AssertNE(binary, "") + var command = gproc.NewProcess(binary, nil) + + testPath := gtest.DataPath("gobuild") + filename := filepath.Join(testPath, "main.go") + output := filepath.Join(testPath, "main.exe") + + command.Args = append(command.Args, "build") + command.Args = append(command.Args, `-ldflags="-X 'main.TestString=\"test string\"'"`) + command.Args = append(command.Args, "-o", output) + command.Args = append(command.Args, filename) + + err := command.Run(gctx.GetInitCtx()) + t.AssertNil(err) + + exists := gfile.Exists(output) + t.Assert(exists, true) + defer gfile.Remove(output) + + runCmd := gproc.NewProcess(output, nil) + var buf strings.Builder + runCmd.Stdout = &buf + runCmd.Stderr = &buf + err = runCmd.Run(gctx.GetInitCtx()) + t.Assert(err, nil) + t.Assert(buf.String(), `"test string"`) + }) + + gtest.C(t, func(t *gtest.T) { + binary := gproc.SearchBinary("go") + t.AssertNE(binary, "") + // NewProcess(path,args) path: It's best not to have spaces + var command = gproc.NewProcess(binary, nil) + + testPath := gtest.DataPath("gobuild") + filename := filepath.Join(testPath, "main.go") + output := filepath.Join(testPath, "main.exe") + + command.Args = append(command.Args, "build") + command.Args = append(command.Args, `-ldflags="-s -w"`) + command.Args = append(command.Args, "-o", output) + command.Args = append(command.Args, filename) + + err := command.Run(gctx.GetInitCtx()) + t.AssertNil(err) + + exists := gfile.Exists(output) + t.Assert(exists, true) + + defer gfile.Remove(output) + }) +} diff --git a/os/gproc/gproc_z_unit_shell_windows_test.go b/os/gproc/gproc_z_unit_shell_windows_test.go new file mode 100644 index 00000000000..9c898528e5f --- /dev/null +++ b/os/gproc/gproc_z_unit_shell_windows_test.go @@ -0,0 +1,124 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +// go test *.go -bench=".*" -benchmem + +//go:build windows + +package gproc_test + +import ( + "fmt" + "path/filepath" + "testing" + + "github.com/gogf/gf/v2/os/gctx" + "github.com/gogf/gf/v2/os/gfile" + "github.com/gogf/gf/v2/os/gproc" + "github.com/gogf/gf/v2/test/gtest" +) + +func Test_ShellExec_GoBuild_Windows(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + testPath := gtest.DataPath("gobuild") + filename := filepath.Join(testPath, "main.go") + output := filepath.Join(testPath, "main.exe") + cmd := fmt.Sprintf(`go build -ldflags="-s -w" -o %s %s`, output, filename) + + err := gproc.ShellRun(gctx.New(), cmd) + t.Assert(err, nil) + + exists := gfile.Exists(output) + t.Assert(exists, true) + + defer gfile.Remove(output) + }) + + gtest.C(t, func(t *gtest.T) { + testPath := gtest.DataPath("gobuild") + filename := filepath.Join(testPath, "main.go") + output := filepath.Join(testPath, "main.exe") + cmd := fmt.Sprintf(`go build -ldflags="-X 'main.TestString=\"test string\"'" -o %s %s`, output, filename) + + err := gproc.ShellRun(gctx.New(), cmd) + t.Assert(err, nil) + + exists := gfile.Exists(output) + t.Assert(exists, true) + defer gfile.Remove(output) + + result, err := gproc.ShellExec(gctx.New(), output) + t.Assert(err, nil) + t.Assert(result, `"test string"`) + }) + +} + +func Test_ShellExec_SpaceDir_Windows(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + testPath := gtest.DataPath("shellexec") + filename := filepath.Join(testPath, "main.go") + // go build -o test.exe main.go + cmd := fmt.Sprintf(`go build -o test.exe %s`, filename) + r, err := gproc.ShellExec(gctx.New(), cmd) + t.AssertNil(err) + t.Assert(r, "") + + exists := gfile.Exists(filename) + t.Assert(exists, true) + + outputDir := filepath.Join(testPath, "testdir") + output := filepath.Join(outputDir, "test.exe") + err = gfile.Move("test.exe", output) + t.AssertNil(err) + defer gfile.Remove(output) + + expectContent := "123" + testOutput := filepath.Join(testPath, "space dir", "test.txt") + cmd = fmt.Sprintf(`%s -c %s -o "%s"`, output, expectContent, testOutput) + r, err = gproc.ShellExec(gctx.New(), cmd) + t.AssertNil(err) + + exists = gfile.Exists(testOutput) + t.Assert(exists, true) + defer gfile.Remove(testOutput) + + contents := gfile.GetContents(testOutput) + t.Assert(contents, expectContent) + }) + gtest.C(t, func(t *gtest.T) { + testPath := gtest.DataPath("shellexec") + filename := filepath.Join(testPath, "main.go") + // go build -o test.exe main.go + cmd := fmt.Sprintf(`go build -o test.exe %s`, filename) + r, err := gproc.ShellExec(gctx.New(), cmd) + t.AssertNil(err) + t.Assert(r, "") + + exists := gfile.Exists(filename) + t.Assert(exists, true) + + outputDir := filepath.Join(testPath, "space dir") + output := filepath.Join(outputDir, "test.exe") + err = gfile.Move("test.exe", output) + t.AssertNil(err) + defer gfile.Remove(output) + + expectContent := "123" + testOutput := filepath.Join(testPath, "testdir", "test.txt") + cmd = fmt.Sprintf(`"%s" -c %s -o %s`, output, expectContent, testOutput) + r, err = gproc.ShellExec(gctx.New(), cmd) + t.AssertNil(err) + + exists = gfile.Exists(testOutput) + t.Assert(exists, true) + defer gfile.Remove(testOutput) + + contents := gfile.GetContents(testOutput) + t.Assert(contents, expectContent) + + }) +} diff --git a/os/gproc/testdata/gobuild/main.go b/os/gproc/testdata/gobuild/main.go new file mode 100644 index 00000000000..9e2072bb5b2 --- /dev/null +++ b/os/gproc/testdata/gobuild/main.go @@ -0,0 +1,15 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package main + +var ( + TestString string +) + +func main() { + print(TestString) +} diff --git a/os/gproc/testdata/shellexec/main.go b/os/gproc/testdata/shellexec/main.go new file mode 100644 index 00000000000..3084d58d18e --- /dev/null +++ b/os/gproc/testdata/shellexec/main.go @@ -0,0 +1,32 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package main + +import ( + "flag" + "fmt" + "os" +) + +func main() { + var content string + var output string + flag.StringVar(&content, "c", "", "写入内容") + flag.StringVar(&output, "o", "", "写入路径") + flag.Parse() + fmt.Println(os.Args) + fmt.Println(content) + fmt.Println(output) + if output != "" { + file, err := os.Create(output) + if err != nil { + panic("create file fail: " + err.Error()) + } + defer file.Close() + file.WriteString(content) + } +}