diff --git a/lib/ansible/runner/__init__.py b/lib/ansible/runner/__init__.py index 12b5d3c030a..7bbbf96a2a1 100644 --- a/lib/ansible/runner/__init__.py +++ b/lib/ansible/runner/__init__.py @@ -389,7 +389,7 @@ class Runner(object): return actual_user - def _count_module_args(self, args): + def _count_module_args(self, args, allow_dupes=False): ''' Count the number of k=v pairs in the supplied module args. This is basically a specialized version of parse_kv() from utils with a few @@ -399,23 +399,26 @@ class Runner(object): if args is not None: args = args.encode('utf-8') try: - lexer = shlex.shlex(args, posix=True) + lexer = shlex.shlex(args) + lexer.whitespace = '\t ' lexer.whitespace_split = True - lexer.quotes = '"' - lexer.ignore_quotes = "'" - vargs = list(lexer) + vargs = [x.decode('utf-8') for x in lexer] except ValueError, ve: if 'no closing quotation' in str(ve).lower(): raise errors.AnsibleError("error parsing argument string '%s', try quoting the entire line." % args) else: raise - vargs = [x.decode('utf-8') for x in vargs] for x in vargs: - if "=" in x: + quoted = x.startswith('"') and x.endswith('"') or x.startswith("'") and x.endswith("'") + if "=" in x and not quoted: k, v = x.split("=",1) - if k in options: - raise errors.AnsibleError("a duplicate parameter was found in the argument string (%s)" % k) - options[k] = v + is_shell_module = self.module_name in ('command', 'shell') + is_shell_param = k in ('creates', 'removes', 'chdir', 'executable') + if k in options and not allow_dupes: + if not(is_shell_module and not is_shell_param): + raise errors.AnsibleError("a duplicate parameter was found in the argument string (%s)" % k) + if is_shell_module and is_shell_param or not is_shell_module: + options[k] = v return len(options) @@ -863,7 +866,7 @@ class Runner(object): # that no variables inadvertantly (or maliciously) add params # to the list of args. We do this by counting the number of k=v # pairs before and after templating. - num_args_pre = self._count_module_args(module_args) + num_args_pre = self._count_module_args(module_args, allow_dupes=True) module_args = template.template(self.basedir, module_args, inject, fail_on_undefined=self.error_on_undefined_vars) num_args_post = self._count_module_args(module_args) if num_args_pre != num_args_post: diff --git a/library/commands/command b/library/commands/command index 69e8362bc7f..0577182e91c 100644 --- a/library/commands/command +++ b/library/commands/command @@ -213,16 +213,21 @@ class CommandModule(AnsibleModule): # use shlex to split up the args, while being careful to preserve # single quotes so they're not removed accidentally - lexer = shlex.shlex(args, posix=True) + lexer = shlex.shlex(args) + lexer.whitespace = '\t ' lexer.whitespace_split = True - lexer.quotes = '"' - lexer.ignore_quotes = "'" items = list(lexer) for x in items: - if '=' in x: + quoted = x.startswith('"') and x.endswith('"') or x.startswith("'") and x.endswith("'") + if '=' in x and not quoted: # check to see if this is a special parameter for the command k, v = x.split('=', 1) + # because we're not breaking out quotes in the shlex split + # above, the value of the k=v pair may still be quoted. If + # so, remove them. + if len(v) > 1 and (v.startswith('"') and v.endswith('"') or v.startswith("'") and v.endswith("'")): + v = v[1:-1] if k in ('creates', 'removes', 'chdir', 'executable', 'NO_LOG'): if k == "chdir": v = os.path.abspath(os.path.expanduser(v)) @@ -235,7 +240,7 @@ class CommandModule(AnsibleModule): params[k] = v # Remove any of the above k=v params from the args string args = PARAM_REGEX.sub('', args) - params['args'] = args + params['args'] = args.strip() return (params, params['args']) main()