diff --git a/lib/ansible/module_utils/splitter.py b/lib/ansible/module_utils/splitter.py index d633c2e289b..849c0714c70 100644 --- a/lib/ansible/module_utils/splitter.py +++ b/lib/ansible/module_utils/splitter.py @@ -101,15 +101,22 @@ def split_args(args): # inside quotation characters tokens = item.split('\n') + line_continuation = False for idx,token in enumerate(tokens): - # if we're at the end of the enumeration, the character separator - # used when reassembling quoted bits should be a space, otherwise - # it will be a newline character + # if there was a newline split, we will re-assemble using + # newlines, otherwise we re-assemble using spaces spacer = ' ' if idx > 0: spacer = '\n' + # if we hit a line continuation character, but + # we're not inside quotes, ignore it and continue + # on to the next token while setting a flag + if token == '\\' and not inside_quotes: + line_continuation = True + continue + # store the previous quoting state for checking later was_inside_quotes = inside_quotes quote_char = _get_quote_state(token, quote_char) @@ -155,7 +162,19 @@ def split_args(args): # finally, if we're at zero depth for all blocks and not inside quotes, and have not # yet appended anything to the list of params, we do so now if not (print_depth or block_depth or comment_depth) and not inside_quotes and not appended and token != '': - params.append(token) + # if the spacer was a newline, prepend this param + # with it so that newlines are preserved, unless + # we previously hit a line continuation character + if spacer == '\n': + if line_continuation: + params.append(token) + else: + params.append('\n%s' % token) + else: + params.append(token) + + # always clear the line continuation flag + line_continuation = False # If we're done and things are not at zero depth or we're still inside quotes, # raise an error to indicate that the args were unbalanced @@ -165,6 +184,7 @@ def split_args(args): # finally, we decode each param back to the unicode it was in the arg string if do_decode: params = [x.decode('utf-8') for x in params] + return params def unquote(data): diff --git a/lib/ansible/utils/__init__.py b/lib/ansible/utils/__init__.py index bb5de5ef35b..7062242d93e 100644 --- a/lib/ansible/utils/__init__.py +++ b/lib/ansible/utils/__init__.py @@ -704,7 +704,7 @@ def parse_kv(args): for x in vargs: if "=" in x: k, v = x.split("=",1) - options[k] = unquote(v.strip()) + options[k.strip()] = unquote(v.strip()) return options def merge_hash(a, b): diff --git a/test/integration/roles/test_command_shell/tasks/main.yml b/test/integration/roles/test_command_shell/tasks/main.yml index db6f14084ef..0ec50193625 100644 --- a/test/integration/roles/test_command_shell/tasks/main.yml +++ b/test/integration/roles/test_command_shell/tasks/main.yml @@ -170,14 +170,17 @@ - "shell_result4.changed == False" - name: execute a shell command using a literal multiline block + args: + executable: /bin/bash shell: | echo this is a "multiline echo" "with a new line - in quotes" - | md5sum - | tr -s ' ' + in quotes" \ + | md5sum \ + | tr -s ' ' \ | cut -f1 -d ' ' + echo "this is a second line" register: shell_result5 - debug: var=shell_result5 @@ -186,5 +189,5 @@ assert: that: - "shell_result5.changed" - - "shell_result5.stdout == '32f3cc201b69ed8afa3902b80f554ca8'" + - "shell_result5.stdout == '32f3cc201b69ed8afa3902b80f554ca8\nthis is a second line'" diff --git a/test/units/TestUtils.py b/test/units/TestUtils.py index b091603626c..49284c52938 100644 --- a/test/units/TestUtils.py +++ b/test/units/TestUtils.py @@ -705,9 +705,9 @@ class TestUtils(unittest.TestCase): # jinja2 loop blocks with lots of complexity _test_combo( # in memory of neighbors cat - # we only preserve newlines inside of quotes - 'a {% if x %} y {%else %} {{meow}} {% endif %} "cookie\nchip"\ndone', - ['a', '{% if x %}', 'y', '{%else %}', '{{meow}}', '{% endif %}', '"cookie\nchip"', 'done'] + # we preserve line breaks unless a line continuation character preceeds them + 'a {% if x %} y {%else %} {{meow}} {% endif %} "cookie\nchip" \\\ndone\nand done', + ['a', '{% if x %}', 'y', '{%else %}', '{{meow}}', '{% endif %}', '"cookie\nchip"', 'done', '\nand', 'done'] ) # test space preservation within quotes