package tools // The shell a command is handed to, which differs by platform: /bin/sh on // Unix, cmd.exe on Windows. Going through a shell is what makes a command in // the file a *command line*: pipes, globs and && all work, so one entry can be // a whole sequence. Splitting an argv here would mean inventing quoting rules // for a file somebody wrote by hand. // Shell returns the program every command is handed to: /bin/sh on Unix, and // on Windows the shell %COMSPEC% names, which is cmd.exe. // // exec.Command(tools.Shell(), tools.ShellArgs("go test ./...")...) func Shell() string { return shellProgram() } // ShellArgs returns the arguments that make the shell run one command line // and exit: ["-c", command] for a Unix shell, ["/S", "/C", command] for // cmd.exe. // // The result is meant for the terminal package, which composes the command // line itself. For an exec.Cmd on Windows the quoting cmd.exe expects is put // in SysProcAttr.CmdLine instead — see childAttributes. func ShellArgs(command string) []string { return shellArgs(command) } // windowsFallbackShell is what runs when COMSPEC is unset, which it never is // on a working Windows: cmd.exe is found through PATH. const windowsFallbackShell = "cmd.exe" // windowsShell returns the shell Windows says is the user's — the value of // COMSPEC — or the fallback when the variable is empty. It is the same rule // the terminal package applies, written twice because tools must not import // terminal. func windowsShell(comspec string) string { if comspec != "" { return comspec } return windowsFallbackShell } // cmdExeCommandLine writes the command line cmd.exe parses by its own rules: // `"cmd.exe" /S /C "command"`. Given /S, cmd.exe strips the first and last // quote of what follows /C and hands the rest to its parser verbatim, so a // quote inside the command reaches it as a quote — the C-runtime spelling Go's // exec would produce, `\"`, would reach it as a backslash and a quote. func cmdExeCommandLine(shell, command string) string { return `"` + shell + `" /S /C "` + command + `"` }