Skip to content

Commit cccd74e

Browse files
committed
escape Windows cmd.exe metacharacters with ^
1 parent 33a7e77 commit cccd74e

File tree

1 file changed

+26
-12
lines changed

1 file changed

+26
-12
lines changed

stdlib/Distributed/src/managers.jl

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ function launch_on_machine(manager::SSHManager, machine, cnt, params, launched,
208208
# shell login (-l) with string command (-c) to launch julia process
209209
remotecmd = shell_escape_posixly(`sh -l -c $cmds`)
210210

211-
elseif params[:shell] == :wincmd
211+
elseif shell == :wincmd
212212
# ssh connects to Windows cmd.exe
213213

214214
# Passing the cookie via ssh stdin currently hangs with
@@ -270,36 +270,39 @@ end
270270

271271
function print_shell_escaped_windows(io, args::AbstractString...)::Nothing
272272
first = true
273+
quoted = false
273274
for arg in args
274275
first || write(io, ' ')
275276
first = false
276277
function isword(c::AbstractChar)
277278
return 'a' <= c <= 'z' || 'A' <= c <= 'Z' || '0' <= c <= '9' ||
278279
c == '\\' || c == '.' || c == '_' || c == '-' || c == ':' ||
279-
c == '/' || c == '=' || c == '"'
280+
c == '/' || c == '=' || c == '"' || c == '%'
280281
end
281282
quotes = !all(isword, arg) || arg == ""
282-
quotes && write(io, '"')
283+
if quotes; write(io, '"'); quoted = !quoted; end
283284
backslashes = 0
284285
for c in arg
285286
if c == '\\'
286287
backslashes += 1
287288
else
288289
if c == '"'
289290
backslashes = backslashes * 2 + 1
291+
quoted = !quoted
290292
end
291293
for j=1:backslashes
292294
write(io, '\\')
293295
end
294296
backslashes = 0
297+
isword(c) || quoted || write(io, '^') # for cmd.exe
295298
write(io, c)
296299
end
297300
end
298301
if quotes; backslashes *= 2; end
299302
for j=1:backslashes
300303
write(io, '\\')
301304
end
302-
quotes && write(io, '"')
305+
if quotes; write(io, '"'); quoted = !quoted; end
303306
end
304307
end
305308

@@ -308,16 +311,27 @@ end
308311
309312
Convert the collection of strings `args` into a Windows command line.
310313
311-
Windows `cmd.exe` passes the entire command line as a single string to
312-
the application (unlike on POSIX systems, where the shell splits the
313-
command line into a list of arguments). Many Windows API applications
314+
Windows passes the entire command line as a single string to the
315+
application (unlike POSIX systems, where the shell splits the command
316+
line into a list of arguments). Many Windows API applications
314317
(including julia.exe), use the conventions of the [Microsoft C
315318
runtime](https://docs.microsoft.com/en-us/cpp/c-language/parsing-c-command-line-arguments)
316-
to split that command line into a list of strings. This function
317-
implements the inverse of that command-line parser. It joins
318-
command-line arguments to be passed to a Windows C/C++/Julia
319-
application into a command line, escaping or quoting meta characters
320-
such as space, double quotes and backslash where needed.
319+
to split that command line into a list of strings.
320+
321+
This function implements the inverse of such a C runtime command-line
322+
parser. It joins command-line arguments to be passed to a Windows
323+
C/C++/Julia application into a command line, escaping or quoting meta
324+
characters such as space, double quotes and backslash where needed.
325+
326+
In addition, this function also escapes meta characters processed by
327+
`cmd.exe`: it places a ^ in front of any potential metacharacter that
328+
follows an even number of quotation marks on the command line.
329+
330+
The percent sign (`%`) is not escaped such that shell variable
331+
references (like `%USER%`) can still be substituted by `cmd.exe`.
332+
333+
Input strings should avoid ASCII control characters, as many of these
334+
cannot be escaped (e.g., NUL, CR, LF).
321335
322336
# Example
323337
```jldoctest

0 commit comments

Comments
 (0)