* powershell - fix quoting values
* Add ignore for smart quote skip
(cherry picked from commit 72a7cb4a2c3036da5e3abb32c50713a262d0c063)
... | ... |
@@ -120,9 +120,9 @@ class ShellModule(ShellBase): |
120 | 120 |
def remove(self, path, recurse=False): |
121 | 121 |
path = self._escape(self._unquote(path)) |
122 | 122 |
if recurse: |
123 |
- return self._encode_script('''Remove-Item "%s" -Force -Recurse;''' % path) |
|
123 |
+ return self._encode_script('''Remove-Item '%s' -Force -Recurse;''' % path) |
|
124 | 124 |
else: |
125 |
- return self._encode_script('''Remove-Item "%s" -Force;''' % path) |
|
125 |
+ return self._encode_script('''Remove-Item '%s' -Force;''' % path) |
|
126 | 126 |
|
127 | 127 |
def mkdtemp(self, basefile=None, system=False, mode=None, tmpdir=None): |
128 | 128 |
# Windows does not have an equivalent for the system temp files, so |
... | ... |
@@ -147,15 +147,15 @@ class ShellModule(ShellBase): |
147 | 147 |
if user_home_path == '~': |
148 | 148 |
script = 'Write-Output (Get-Location).Path' |
149 | 149 |
elif user_home_path.startswith('~\\'): |
150 |
- script = 'Write-Output ((Get-Location).Path + "%s")' % self._escape(user_home_path[1:]) |
|
150 |
+ script = "Write-Output ((Get-Location).Path + '%s')" % self._escape(user_home_path[1:]) |
|
151 | 151 |
else: |
152 |
- script = 'Write-Output "%s"' % self._escape(user_home_path) |
|
152 |
+ script = "Write-Output '%s'" % self._escape(user_home_path) |
|
153 | 153 |
return self._encode_script(script) |
154 | 154 |
|
155 | 155 |
def exists(self, path): |
156 | 156 |
path = self._escape(self._unquote(path)) |
157 | 157 |
script = ''' |
158 |
- If (Test-Path "%s") |
|
158 |
+ If (Test-Path '%s') |
|
159 | 159 |
{ |
160 | 160 |
$res = 0; |
161 | 161 |
} |
... | ... |
@@ -163,7 +163,7 @@ class ShellModule(ShellBase): |
163 | 163 |
{ |
164 | 164 |
$res = 1; |
165 | 165 |
} |
166 |
- Write-Output "$res"; |
|
166 |
+ Write-Output '$res'; |
|
167 | 167 |
Exit $res; |
168 | 168 |
''' % path |
169 | 169 |
return self._encode_script(script) |
... | ... |
@@ -171,14 +171,14 @@ class ShellModule(ShellBase): |
171 | 171 |
def checksum(self, path, *args, **kwargs): |
172 | 172 |
path = self._escape(self._unquote(path)) |
173 | 173 |
script = ''' |
174 |
- If (Test-Path -PathType Leaf "%(path)s") |
|
174 |
+ If (Test-Path -PathType Leaf '%(path)s') |
|
175 | 175 |
{ |
176 | 176 |
$sp = new-object -TypeName System.Security.Cryptography.SHA1CryptoServiceProvider; |
177 |
- $fp = [System.IO.File]::Open("%(path)s", [System.IO.Filemode]::Open, [System.IO.FileAccess]::Read); |
|
177 |
+ $fp = [System.IO.File]::Open('%(path)s', [System.IO.Filemode]::Open, [System.IO.FileAccess]::Read); |
|
178 | 178 |
[System.BitConverter]::ToString($sp.ComputeHash($fp)).Replace("-", "").ToLower(); |
179 | 179 |
$fp.Dispose(); |
180 | 180 |
} |
181 |
- ElseIf (Test-Path -PathType Container "%(path)s") |
|
181 |
+ ElseIf (Test-Path -PathType Container '%(path)s') |
|
182 | 182 |
{ |
183 | 183 |
Write-Output "3"; |
184 | 184 |
} |
... | ... |
@@ -264,22 +264,11 @@ class ShellModule(ShellBase): |
264 | 264 |
return m.group(1) |
265 | 265 |
return value |
266 | 266 |
|
267 |
- def _escape(self, value, include_vars=False): |
|
268 |
- '''Return value escaped for use in PowerShell command.''' |
|
269 |
- # http://www.techotopia.com/index.php/Windows_PowerShell_1.0_String_Quoting_and_Escape_Sequences |
|
270 |
- # http://stackoverflow.com/questions/764360/a-list-of-string-replacements-in-python |
|
271 |
- subs = [('\n', '`n'), ('\r', '`r'), ('\t', '`t'), ('\a', '`a'), |
|
272 |
- ('\b', '`b'), ('\f', '`f'), ('\v', '`v'), ('"', '`"'), |
|
273 |
- ('\'', '`\''), ('`', '``'), ('\x00', '`0')] |
|
274 |
- if include_vars: |
|
275 |
- subs.append(('$', '`$')) |
|
276 |
- pattern = '|'.join('(%s)' % re.escape(p) for p, s in subs) |
|
277 |
- substs = [s for p, s in subs] |
|
278 |
- |
|
279 |
- def replace(m): |
|
280 |
- return substs[m.lastindex - 1] |
|
281 |
- |
|
282 |
- return re.sub(pattern, replace, value) |
|
267 |
+ def _escape(self, value): |
|
268 |
+ '''Return value escaped for use in PowerShell single quotes.''' |
|
269 |
+ # There are 5 chars that need to be escaped in a single quote. |
|
270 |
+ # https://github.com/PowerShell/PowerShell/blob/b7cb335f03fe2992d0cbd61699de9d9aafa1d7c1/src/System.Management.Automation/engine/parser/CharTraits.cs#L265-L272 |
|
271 |
+ return re.compile(u"(['\u2018\u2019\u201a\u201b])").sub(u'\\1\\1', value) |
|
283 | 272 |
|
284 | 273 |
def _encode_script(self, script, as_list=False, strict_mode=True, preserve_rc=True): |
285 | 274 |
'''Convert a PowerShell script to a single base64-encoded command.''' |
... | ... |
@@ -187,3 +187,26 @@ |
187 | 187 |
# Doesn't fail anymore, only returns a message. |
188 | 188 |
- "fetch_dir is not changed" |
189 | 189 |
- "fetch_dir.msg" |
190 |
+ |
|
191 |
+- name: create file with special characters |
|
192 |
+ raw: Set-Content -LiteralPath '{{ remote_tmp_dir }}\abc$not var''quote‘‘' -Value 'abc' |
|
193 |
+ |
|
194 |
+- name: fetch file with special characters |
|
195 |
+ fetch: |
|
196 |
+ src: '{{ remote_tmp_dir }}\abc$not var''quote‘' |
|
197 |
+ dest: '{{ host_output_dir }}/' |
|
198 |
+ flat: yes |
|
199 |
+ register: fetch_special_file |
|
200 |
+ |
|
201 |
+- name: get content of fetched file |
|
202 |
+ command: cat {{ (host_output_dir ~ "/abc$not var'quote‘") | quote }} |
|
203 |
+ register: fetch_special_file_actual |
|
204 |
+ delegate_to: localhost |
|
205 |
+ |
|
206 |
+- name: assert fetch file with special characters |
|
207 |
+ assert: |
|
208 |
+ that: |
|
209 |
+ - fetch_special_file is changed |
|
210 |
+ - fetch_special_file.checksum == '34d4150adc3347f1dd8ce19fdf65b74d971ab602' |
|
211 |
+ - fetch_special_file.dest == host_output_dir + "/abc$not var'quote‘" |
|
212 |
+ - fetch_special_file_actual.stdout == 'abc' |
... | ... |
@@ -304,6 +304,7 @@ test/integration/targets/want_json_modules_posix/library/helloworld.py future-im |
304 | 304 |
test/integration/targets/want_json_modules_posix/library/helloworld.py metaclass-boilerplate |
305 | 305 |
test/integration/targets/win_exec_wrapper/library/test_fail.ps1 pslint:PSCustomUseLiteralPath |
306 | 306 |
test/integration/targets/win_exec_wrapper/tasks/main.yml no-smart-quotes # We are explicitly testing smart quote support for env vars |
307 |
+test/integration/targets/win_fetch/tasks/main.yml no-smart-quotes # We are explictly testing smart quotes in the file name to fetch |
|
307 | 308 |
test/integration/targets/win_module_utils/library/legacy_only_new_way_win_line_ending.ps1 line-endings # Explicitly tests that we still work with Windows line endings |
308 | 309 |
test/integration/targets/win_module_utils/library/legacy_only_old_way_win_line_ending.ps1 line-endings # Explicitly tests that we still work with Windows line endings |
309 | 310 |
test/integration/targets/win_script/files/test_script.ps1 pslint:PSAvoidUsingWriteHost # Keep |