@@ -17,11 +17,44 @@ type Result struct {
1717 Duration time.Duration
1818}
1919
20+ // shellBuiltins lists commands that are shell built-ins and cannot be
21+ // executed directly via exec.Command.
22+ var shellBuiltins = map [string ]bool {
23+ "export" : true ,
24+ "unset" : true ,
25+ "source" : true ,
26+ "alias" : true ,
27+ "unalias" : true ,
28+ "cd" : true ,
29+ "eval" : true ,
30+ "set" : true ,
31+ "shopt" : true ,
32+ "declare" : true ,
33+ "local" : true ,
34+ "readonly" : true ,
35+ "typeset" : true ,
36+ "ulimit" : true ,
37+ "umask" : true ,
38+ }
39+
40+ // makeCommand creates an exec.Cmd, wrapping shell built-ins with sh -c
41+ // so they can be executed. Shell built-ins like "export" have no binary
42+ // in $PATH and would fail with exec.Command directly.
43+ func makeCommand (command string , args []string ) * exec.Cmd {
44+ if shellBuiltins [command ] {
45+ shArgs := make ([]string , 0 , len (args )+ 3 )
46+ shArgs = append (shArgs , "-c" , command + ` "$@"` , "_" )
47+ shArgs = append (shArgs , args ... )
48+ return exec .Command ("sh" , shArgs ... )
49+ }
50+ return exec .Command (command , args ... )
51+ }
52+
2053// Execute runs a command, capturing stdout and stderr concurrently via goroutines.
2154func Execute (command string , args []string ) (* Result , error ) {
2255 start := time .Now ()
2356
24- cmd := exec . Command (command , args ... )
57+ cmd := makeCommand (command , args )
2558 // Don't connect stdin for captured commands — prevents blocking on
2659 // commands that don't read stdin (most filtered commands).
2760 // Passthrough commands still get stdin via the Passthrough function.
@@ -74,7 +107,7 @@ func Execute(command string, args []string) (*Result, error) {
74107
75108// Passthrough runs a command with inherited stdio (no capture).
76109func Passthrough (command string , args []string ) (int , error ) {
77- cmd := exec . Command (command , args ... )
110+ cmd := makeCommand (command , args )
78111 cmd .Stdin = os .Stdin
79112 cmd .Stdout = os .Stdout
80113 cmd .Stderr = os .Stderr
0 commit comments