Skip to content

Commit f3a98b7

Browse files
matthijskooijmancmaglie
authored andcommitted
Let utils.ExecCommand print the command in verbose mode
Previously, the command was printed by PrepareCommandForRecipe. Letting ExecCommand print seems more accurate, since it is only printed when it is actually run (though this already happened in practice). Additionally, the command can now be modified between PrepareCommandForRecipe and ExecCommand while preserving correct output. Since ExecCommand deals with a slice of arguments instead of a single command string, this requires merging them together into a proper commandline. Some care is taken to quote arguments containing spaces, quotes or backslashes, though this is mostly intended for display purposes. Arguments are only quoted when needed, regardless of whether they were quoted in the original pattern. Signed-off-by: Matthijs Kooijman <[email protected]>
1 parent af2ed21 commit f3a98b7

File tree

2 files changed

+46
-5
lines changed

2 files changed

+46
-5
lines changed

Diff for: test/utils_test.go

+19
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,25 @@ func TestCommandLineParser(t *testing.T) {
7070
require.Equal(t, "/tmp/sketch321469072.cpp", parts[22])
7171
}
7272

73+
func TestPrintableCommand(t *testing.T) {
74+
parts := []string{
75+
"/path/to/dir with spaces/cmd",
76+
"arg1",
77+
"arg-\"with\"-quotes",
78+
"specialchar-`~!@#$%^&*()-_=+[{]}\\|;:'\",<.>/?-argument",
79+
"arg with spaces",
80+
"arg\twith\t\ttabs",
81+
"lastarg",
82+
}
83+
correct := "\"/path/to/dir with spaces/cmd\"" +
84+
" arg1 \"arg-\\\"with\\\"-quotes\"" +
85+
" \"specialchar-`~!@#$%^&*()-_=+[{]}\\\\|;:'\\\",<.>/?-argument\"" +
86+
" \"arg with spaces\" \"arg\twith\t\ttabs\"" +
87+
" lastarg"
88+
result := utils.PrintableCommand(parts)
89+
require.Equal(t, correct, result)
90+
}
91+
7392
func TestCommandLineParserError(t *testing.T) {
7493
command := "\"command missing quote"
7594

Diff for: utils/utils.go

+27-5
Original file line numberDiff line numberDiff line change
@@ -277,14 +277,36 @@ func PrepareCommand(pattern string, logger i18n.Logger, relativePath string) (*e
277277
return PrepareCommandFilteredArgs(pattern, filterEmptyArg, logger, relativePath)
278278
}
279279

280+
func printableArgument(arg string) string {
281+
if strings.ContainsAny(arg, "\"\\ \t") {
282+
arg = strings.Replace(arg, "\\", "\\\\", -1)
283+
arg = strings.Replace(arg, "\"", "\\\"", -1)
284+
return "\"" + arg + "\""
285+
} else {
286+
return arg
287+
}
288+
}
289+
290+
// Convert a command and argument slice back to a printable string.
291+
// This adds basic escaping which is sufficient for debug output, but
292+
// probably not for shell interpretation. This essentially reverses
293+
// ParseCommandLine.
294+
func PrintableCommand(parts []string) string {
295+
return strings.Join(Map(parts, printableArgument), " ")
296+
}
297+
280298
const (
281-
Ignore = 0 // Redirect to null
282-
Show = 1 // Show on stdout/stderr as normal
299+
Ignore = 0 // Redirect to null
300+
Show = 1 // Show on stdout/stderr as normal
283301
ShowIfVerbose = 2 // Show if verbose is set, Ignore otherwise
284-
Capture = 3 // Capture into buffer
302+
Capture = 3 // Capture into buffer
285303
)
286304

287305
func ExecCommand(ctx *types.Context, command *exec.Cmd, stdout int, stderr int) ([]byte, []byte, error) {
306+
if ctx.Verbose {
307+
fmt.Println(PrintableCommand(command.Args))
308+
}
309+
288310
if stdout == Capture {
289311
buffer := &bytes.Buffer{}
290312
command.Stdout = buffer
@@ -308,10 +330,10 @@ func ExecCommand(ctx *types.Context, command *exec.Cmd, stdout int, stderr int)
308330

309331
var outbytes, errbytes []byte
310332
if buf, ok := command.Stdout.(*bytes.Buffer); ok {
311-
outbytes = buf.Bytes()
333+
outbytes = buf.Bytes()
312334
}
313335
if buf, ok := command.Stderr.(*bytes.Buffer); ok {
314-
errbytes = buf.Bytes()
336+
errbytes = buf.Bytes()
315337
}
316338

317339
return outbytes, errbytes, i18n.WrapError(err)

0 commit comments

Comments
 (0)