| 🛟 Updated. 28d5985 k33g 17h ago | 1 | package tools |
| 2 | |
| 3 | // The shell a command is handed to, which differs by platform: /bin/sh on |
| 4 | // Unix, cmd.exe on Windows. Going through a shell is what makes a command in |
| 5 | // the file a *command line*: pipes, globs and && all work, so one entry can be |
| 6 | // a whole sequence. Splitting an argv here would mean inventing quoting rules |
| 7 | // for a file somebody wrote by hand. |
| 8 | |
| 9 | // Shell returns the program every command is handed to: /bin/sh on Unix, and |
| 10 | // on Windows the shell %COMSPEC% names, which is cmd.exe. |
| 11 | // |
| 12 | // exec.Command(tools.Shell(), tools.ShellArgs("go test ./...")...) |
| 13 | func Shell() string { return shellProgram() } |
| 14 | |
| 15 | // ShellArgs returns the arguments that make the shell run one command line |
| 16 | // and exit: ["-c", command] for a Unix shell, ["/S", "/C", command] for |
| 17 | // cmd.exe. |
| 18 | // |
| 19 | // The result is meant for the terminal package, which composes the command |
| 20 | // line itself. For an exec.Cmd on Windows the quoting cmd.exe expects is put |
| 21 | // in SysProcAttr.CmdLine instead — see childAttributes. |
| 22 | func ShellArgs(command string) []string { return shellArgs(command) } |
| 23 | |
| 24 | // windowsFallbackShell is what runs when COMSPEC is unset, which it never is |
| 25 | // on a working Windows: cmd.exe is found through PATH. |
| 26 | const windowsFallbackShell = "cmd.exe" |
| 27 | |
| 28 | // windowsShell returns the shell Windows says is the user's — the value of |
| 29 | // COMSPEC — or the fallback when the variable is empty. It is the same rule |
| 30 | // the terminal package applies, written twice because tools must not import |
| 31 | // terminal. |
| 32 | func windowsShell(comspec string) string { |
| 33 | if comspec != "" { |
| 34 | return comspec |
| 35 | } |
| 36 | return windowsFallbackShell |
| 37 | } |
| 38 | |
| 39 | // cmdExeCommandLine writes the command line cmd.exe parses by its own rules: |
| 40 | // `"cmd.exe" /S /C "command"`. Given /S, cmd.exe strips the first and last |
| 41 | // quote of what follows /C and hands the rest to its parser verbatim, so a |
| 42 | // quote inside the command reaches it as a quote — the C-runtime spelling Go's |
| 43 | // exec would produce, `\"`, would reach it as a backslash and a quote. |
| 44 | func cmdExeCommandLine(shell, command string) string { |
| 45 | return `"` + shell + `" /S /C "` + command + `"` |
| 46 | } |