2
2
3
3
# # shell-like command parsing ##
4
4
5
- function shell_parse (raw:: AbstractString , interp:: Bool )
6
- s = lstrip (raw)
7
- # Strips the end but respects the space when the string endswith "\\ "
5
+ const shell_special = " #{}()[]<>|&*?~;"
6
+
7
+ function shell_parse (str:: AbstractString , interpolate:: Bool = true )
8
+ s = lstrip (str)
9
+ # strips the end but respects the space when the string ends with "\\ "
8
10
r = RevString (s)
9
11
i = start (r)
10
12
c_old = nothing
@@ -22,7 +24,7 @@ function shell_parse(raw::AbstractString, interp::Bool)
22
24
s = s[1 : end - i+ 1 ]
23
25
24
26
last_parse = 0 : - 1
25
- isempty (s) && return interp ? (Expr (:tuple ,:()),last_parse) : ([],last_parse)
27
+ isempty (s) && return interpolate ? (Expr (:tuple ,:()),last_parse) : ([],last_parse)
26
28
27
29
in_single_quotes = false
28
30
in_double_quotes = false
@@ -57,7 +59,7 @@ function shell_parse(raw::AbstractString, interp::Bool)
57
59
end
58
60
j = k
59
61
end
60
- elseif interp && ! in_single_quotes && c == ' $'
62
+ elseif interpolate && ! in_single_quotes && c == ' $'
61
63
update_arg (s[i: j- 1 ]); i = k; j = k
62
64
if done (s,k)
63
65
error (" \$ right before end of command" )
@@ -92,6 +94,8 @@ function shell_parse(raw::AbstractString, interp::Bool)
92
94
update_arg (s[i: j- 1 ]); i = k
93
95
c, k = next (s,k)
94
96
end
97
+ elseif ! in_single_quotes && ! in_double_quotes && c in shell_special
98
+ depwarn (" special characters \" $shell_special \" should now be quoted in commands" , :shell_parse )
95
99
end
96
100
j = k
97
101
end
@@ -103,18 +107,15 @@ function shell_parse(raw::AbstractString, interp::Bool)
103
107
update_arg (s[i: end ])
104
108
append_arg ()
105
109
106
- if ! interp
107
- return (args,last_parse)
108
- end
110
+ interpolate || return args, last_parse
109
111
110
112
# construct an expression
111
113
ex = Expr (:tuple )
112
114
for arg in args
113
115
push! (ex. args, Expr (:tuple , arg... ))
114
116
end
115
- ( ex,last_parse)
117
+ return ex, last_parse
116
118
end
117
- shell_parse (s:: AbstractString ) = shell_parse (s,true )
118
119
119
120
function shell_split (s:: AbstractString )
120
121
parsed = shell_parse (s,false )[1 ]
@@ -125,14 +126,14 @@ function shell_split(s::AbstractString)
125
126
args
126
127
end
127
128
128
- function print_shell_word (io:: IO , word:: AbstractString )
129
+ function print_shell_word (io:: IO , word:: AbstractString , special :: AbstractString = shell_special )
129
130
if isempty (word)
130
131
print (io, " ''" )
131
132
end
132
133
has_single = false
133
134
has_special = false
134
135
for c in word
135
- if isspace (c) || c== ' \\ ' || c== ' \' ' || c== ' "' || c== ' $'
136
+ if isspace (c) || c== ' \\ ' || c== ' \' ' || c== ' "' || c== ' $' || c in special
136
137
has_special = true
137
138
if c == ' \' '
138
139
has_single = true
@@ -155,13 +156,32 @@ function print_shell_word(io::IO, word::AbstractString)
155
156
end
156
157
end
157
158
158
- function print_shell_escaped (io:: IO , cmd:: AbstractString , args:: AbstractString... )
159
- print_shell_word (io, cmd)
159
+ function print_shell_escaped (
160
+ io:: IO , cmd:: AbstractString , args:: AbstractString... ;
161
+ special:: AbstractString = shell_special
162
+ )
163
+ print_shell_word (io, cmd, special)
160
164
for arg in args
161
165
print (io, ' ' )
162
- print_shell_word (io, arg)
166
+ print_shell_word (io, arg, special )
163
167
end
164
168
end
165
- print_shell_escaped (io:: IO ) = nothing
169
+ print_shell_escaped (io:: IO ; special:: String = shell_special) = nothing
170
+
171
+ """
172
+ shell_escape(args::Union{Cmd,AbstractString...}; special::AbstractString="$shell_special ")
173
+
174
+ The unexported `shell_escape` function is the inverse of the unexported `shell_split` function:
175
+ it takes a string or command object and escapes any special characters in such a way that calling
176
+ `shell_split` on it would give back the array of words in the original command. The `special`
177
+ keyword argument controls what characters in addition to whitespace, backslashes, quotes and
178
+ dollar signs are considered to be special. Examples:
179
+
180
+ julia> Base.shell_escape("echo", "this", "&&", "that")
181
+ "echo this '&&' that"
166
182
167
- shell_escape (args:: AbstractString... ) = sprint (print_shell_escaped, args... )
183
+ julia> Base.shell_escape("cat", "/foo/bar baz", "&&", "echo", "done", special="")
184
+ "cat '/foo/bar baz' && echo done"
185
+ """
186
+ shell_escape (args:: AbstractString... ; special:: AbstractString = shell_special) =
187
+ sprint (io-> print_shell_escaped (io, args... , special= special))
0 commit comments