Mercurial > public > mercurial-scm > hg-stable
diff mercurial/dispatch.py @ 22158:bc2132dfc0a4
alias: expand "$@" as list of parameters quoted individually (BC) (issue4200)
Before this patch, there was no way to pass in all the positional parameters as
separate words down to another command.
(1) $@ (without quotes) would expand to all the parameters separated by a space.
This would work fine for arguments without spaces, but arguments with spaces
in them would be split up by POSIX shells into separate words.
(2) '$@' (in single quotes) would expand to all the parameters within a pair of
single quotes. POSIX shells would then treat the entire list of arguments
as one word.
(3) "$@" (in double quotes) would expand similarly to (2).
With this patch, we expand "$@" (in double quotes) as all positional
parameters, quoted individually with util.shellquote, and separated by spaces.
Under standard field-splitting conditions, POSIX shells will tokenize each
argument into exactly one word.
This is a backwards-incompatible change, but the old behavior was arguably a
bug: Bourne-derived shells have expanded "$@" as a tokenized list of positional
parameters for a very long time. I could find this behavior specified in IEEE
Std 1003.1-2001, and this probably goes back to much further before that.
author | Siddharth Agarwal <sid0@fb.com> |
---|---|
date | Wed, 13 Aug 2014 23:21:52 -0700 |
parents | af15de6775c7 |
children | 645457f73aa6 |
line wrap: on
line diff
--- a/mercurial/dispatch.py Wed Aug 13 22:37:09 2014 -0700 +++ b/mercurial/dispatch.py Wed Aug 13 23:21:52 2014 -0700 @@ -331,6 +331,27 @@ args = shlex.split(cmd) return args + givenargs +def aliasinterpolate(name, args, cmd): + '''interpolate args into cmd for shell aliases + + This also handles $0, $@ and "$@". + ''' + # util.interpolate can't deal with "$@" (with quotes) because it's only + # built to match prefix + patterns. + replacemap = dict(('$%d' % (i + 1), arg) for i, arg in enumerate(args)) + replacemap['$0'] = name + replacemap['$$'] = '$' + replacemap['$@'] = ' '.join(args) + # Typical Unix shells interpolate "$@" (with quotes) as all the positional + # parameters, separated out into words. Emulate the same behavior here by + # quoting the arguments individually. POSIX shells will then typically + # tokenize each argument into exactly one word. + replacemap['"$@"'] = ' '.join(util.shellquote(arg) for arg in args) + # escape '\$' for regex + regex = '|'.join(replacemap.keys()).replace('$', r'\$') + r = re.compile(regex) + return r.sub(lambda x: replacemap[x.group()], cmd) + class cmdalias(object): def __init__(self, name, definition, cmdtable): self.name = self.cmd = name @@ -376,10 +397,7 @@ % (int(m.groups()[0]), self.name)) return '' cmd = re.sub(r'\$(\d+|\$)', _checkvar, self.definition[1:]) - replace = dict((str(i + 1), arg) for i, arg in enumerate(args)) - replace['0'] = self.name - replace['@'] = ' '.join(args) - cmd = util.interpolate(r'\$', replace, cmd, escape_prefix=True) + cmd = aliasinterpolate(self.name, args, cmd) return util.system(cmd, environ=env, out=ui.fout) self.fn = fn return