Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

proposal: os: add a way to start process from full command line #29841

Closed
hallazzang opened this issue Jan 20, 2019 · 6 comments
Closed

proposal: os: add a way to start process from full command line #29841

hallazzang opened this issue Jan 20, 2019 · 6 comments

Comments

@hallazzang
Copy link
Contributor

Currently it's not possible to start a process directly from full command line:

argv0p, err := UTF16PtrFromString(argv0)
if err != nil {
return 0, 0, err
}

if sys.Token != 0 {
err = CreateProcessAsUser(sys.Token, argv0p, argvp, nil, nil, true, flags, createEnvBlock(attr.Env), dirp, si, pi)
} else {
err = CreateProcess(argv0p, argvp, nil, nil, true, flags, createEnvBlock(attr.Env), dirp, si, pi)
}

In the code above, argv0p wouldn't be nil, thus we can't use the functionality that CreateProcess(AsUser) API gives, by passing lpApplicationName a NULL to use lpCommandLine only.

Such kind of process is needed when dealing with, for example, UninstallString registry value. Keys under HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall usually have UninstallString value, and that is just a command line string to execute when uninstalling a software(within control panel). And to execute that command line, I have to use CreateProcess Windows API directly, without seeing benefits of using os.StartProcess or exec.Command api.

Even using syscall.SysProcAttr.CmdLine, this behavior cannot be achieved. It is still needed to parse executable path manually.

Actually, there is a function commandLineToArgv that converts full command line into os.StartProcess-cousumable chunks:

go/src/os/exec_windows.go

Lines 159 to 174 in ff7b245

// commandLineToArgv splits a command line into individual argument
// strings, following the Windows conventions documented
// at http://daviddeley.com/autohotkey/parameters/parameters.htm#WINARGV
func commandLineToArgv(cmd string) []string {
var args []string
for len(cmd) > 0 {
if cmd[0] == ' ' || cmd[0] == '\t' {
cmd = cmd[1:]
continue
}
var arg []byte
arg, cmd = readNextArg(cmd)
args = append(args, string(arg))
}
return args
}

and works well for this situation(https://play.golang.org/p/lRhDlagni41), but unfortunately it is not exported.

So it would be good to have commandLineToArgv function exported, or to have seperate api that acts like system() function in C. Thanks.

@gopherbot gopherbot added this to the Proposal milestone Jan 20, 2019
@bradfitz
Copy link
Contributor

/cc @alexbrainman @jordanrh1

@jordanrh1
Copy link
Contributor

What if you pass cmd.exe as the application name and "/c" as the first argument?

@hallazzang
Copy link
Contributor Author

@jordanrh1 Ok, it works.

package main

import (
	"fmt"
	"os"
	"os/exec"
	"syscall"
)

func main() {
	if len(os.Args) > 1 {
		for i, arg := range os.Args[1:] {
			fmt.Printf("arg #%d: %s\n", i, arg)
		}
		return
	}

	p, _ := os.Executable()
	cmdLine := fmt.Sprintf(`"%s" foo "bar"`, p)

	fmt.Printf("cmdLine: %s\n", cmdLine)

	cmd := exec.Command("cmd.exe")
	cmd.SysProcAttr = &syscall.SysProcAttr{CmdLine: fmt.Sprintf(`/c "%s"`, cmdLine)}
	output, err := cmd.CombinedOutput()
	fmt.Printf("output:\n%s\n", output)
	if err != nil {
		fmt.Printf("error: %+v\n", err)
	}
}

Output:

cmdLine: "C:\Users\...\main.exe" foo "bar"
output:
arg #0: foo
arg #1: bar

I think it'd be good to have this method documented somewhere. Thanks.

@hallazzang
Copy link
Contributor Author

BTW, this method spawns cmd.exe and then executes actual command, seems not the perfect way to get the job done. In my own opinion it's still worth having separate API.

@jordanrh1
Copy link
Contributor

I'm glad it worked for you. In this case, since you're given a full command line string, I think cmd.exe is the better option since you can pass the command line string unmodified to cmd.exe, and let the system parse it. I think the overhead of spawning cmd.exe is quite small since the system does this sort of thing all the time.

@hallazzang
Copy link
Contributor Author

Got it, I'll close this issue. Thank you for your support.

@golang golang locked and limited conversation to collaborators Jan 26, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

5 participants