@@ -15,29 +15,66 @@ defmodule Mix.Tasks.Cmd do
15
15
16
16
$ mix cmd make
17
17
18
- By passing the `--cd` flag before the command, you can also force
19
- the command to run in a specific directory:
18
+ ## Shell expansion
20
19
21
- $ mix cmd --cd "third-party" make
20
+ When you invoke `mix cmd` from the command line, environment variables,
21
+ glob patterns, and similar are expanded by the current shell and then
22
+ passed to `mix cmd`. For example, if you invoke:
23
+
24
+ $ mix cmd echo lib/*
25
+
26
+ Your shell will expand "lib/*" and then pass multiple arguments to
27
+ `mix cmd`, which in turn passes them to `echo`. Note that, `mix cmd`
28
+ by itself, does not perform any shell expansion. This means that,
29
+ if you invoke `mix cmd` programatically, as in:
30
+
31
+ Mix.Task.run("cmd", ["echo", "lib/*"])
32
+
33
+ or through a `mix.exs` alias:
34
+
35
+ alias: "cmd echo lib/*"
36
+
37
+ It will literally print "lib/*".
38
+
39
+ This behaviour is the default from Elixir v1.19. If you want
40
+ `mix cmd` to behave like a shell, you can pass the `--shell`
41
+ option before the command name:
42
+
43
+ Mix.Task.run("cmd", ["--shell", "echo", "lib/*"])
44
+
45
+ Keep in mind however that, if `--shell` is given, quoted arguments
46
+ are no longer correctly propagated to the underlying command
47
+ (as they get expanded before hand).
48
+
49
+ This task is automatically re-enabled, so it can be called multiple times
50
+ with different arguments.
51
+
52
+ ## Umbrella applications and directories
22
53
23
54
This task is also useful in umbrella applications to execute a command
24
55
on each child app:
25
56
26
57
$ mix cmd pwd
27
58
59
+ In such cases, Mix will change the current working directory to the root
60
+ of each umbrella application before executing the command.
61
+
28
62
You can limit which apps the cmd runs in by using `mix do` with the `--app`
29
63
option:
30
64
31
65
$ mix do --app app1 --app app2 cmd pwd
32
66
33
- The tasks aborts whenever a command exits with a non-zero status.
67
+ Note the tasks aborts whenever a command exits with a non-zero status.
34
68
35
- This task is automatically re-enabled, so it can be called multiple times
36
- with different arguments.
69
+ Outside of umbrella projects, you can pass the `--cd` flag before the command,
70
+ to specify a directory:
71
+
72
+ $ mix cmd --cd "third-party" make
37
73
38
74
## Command line options
39
75
40
76
* `--cd` *(since v1.10.4)* - the directory to run the command in
77
+ * `--shell` *(since v1.19.0)* - perform shell expansion of the arguments
41
78
42
79
## Orphan operating system processes
43
80
@@ -54,7 +91,8 @@ defmodule Mix.Tasks.Cmd do
54
91
55
92
@ switches [
56
93
app: :keep ,
57
- cd: :string
94
+ cd: :string ,
95
+ shell: :boolean
58
96
]
59
97
60
98
@ impl true
@@ -78,16 +116,21 @@ defmodule Mix.Tasks.Cmd do
78
116
path = hd ( args )
79
117
rest = tl ( args )
80
118
81
- path =
82
- if String . contains? ( path , [ "/" , "\\ " ] ) and Path . type ( path ) != :absolute do
83
- Path . expand ( path , Keyword . get ( opts , :cd , "." ) )
84
- else
85
- path
119
+ arg =
120
+ cond do
121
+ Keyword . get ( opts , :shell , false ) ->
122
+ Enum . join ( [ path | rest ] , " " )
123
+
124
+ String . contains? ( path , [ "/" , "\\ " ] ) and Path . type ( path ) != :absolute ->
125
+ { Path . expand ( path , Keyword . get ( opts , :cd , "." ) ) , rest }
126
+
127
+ true ->
128
+ { path , rest }
86
129
end
87
130
88
131
cmd_opts = Keyword . take ( opts , [ :cd ] )
89
132
90
- case Mix . shell ( ) . cmd ( { path , rest } , cmd_opts ) do
133
+ case Mix . shell ( ) . cmd ( arg , cmd_opts ) do
91
134
0 -> :ok
92
135
status -> exit ( status )
93
136
end
0 commit comments