Mercurial > hg
comparison 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 |
comparison
equal
deleted
inserted
replaced
22157:bd45d92883f9 | 22158:bc2132dfc0a4 |
---|---|
329 givenargs = [x for i, x in enumerate(givenargs) | 329 givenargs = [x for i, x in enumerate(givenargs) |
330 if i not in nums] | 330 if i not in nums] |
331 args = shlex.split(cmd) | 331 args = shlex.split(cmd) |
332 return args + givenargs | 332 return args + givenargs |
333 | 333 |
334 def aliasinterpolate(name, args, cmd): | |
335 '''interpolate args into cmd for shell aliases | |
336 | |
337 This also handles $0, $@ and "$@". | |
338 ''' | |
339 # util.interpolate can't deal with "$@" (with quotes) because it's only | |
340 # built to match prefix + patterns. | |
341 replacemap = dict(('$%d' % (i + 1), arg) for i, arg in enumerate(args)) | |
342 replacemap['$0'] = name | |
343 replacemap['$$'] = '$' | |
344 replacemap['$@'] = ' '.join(args) | |
345 # Typical Unix shells interpolate "$@" (with quotes) as all the positional | |
346 # parameters, separated out into words. Emulate the same behavior here by | |
347 # quoting the arguments individually. POSIX shells will then typically | |
348 # tokenize each argument into exactly one word. | |
349 replacemap['"$@"'] = ' '.join(util.shellquote(arg) for arg in args) | |
350 # escape '\$' for regex | |
351 regex = '|'.join(replacemap.keys()).replace('$', r'\$') | |
352 r = re.compile(regex) | |
353 return r.sub(lambda x: replacemap[x.group()], cmd) | |
354 | |
334 class cmdalias(object): | 355 class cmdalias(object): |
335 def __init__(self, name, definition, cmdtable): | 356 def __init__(self, name, definition, cmdtable): |
336 self.name = self.cmd = name | 357 self.name = self.cmd = name |
337 self.cmdname = '' | 358 self.cmdname = '' |
338 self.definition = definition | 359 self.definition = definition |
374 ui.debug("No argument found for substitution " | 395 ui.debug("No argument found for substitution " |
375 "of %i variable in alias '%s' definition." | 396 "of %i variable in alias '%s' definition." |
376 % (int(m.groups()[0]), self.name)) | 397 % (int(m.groups()[0]), self.name)) |
377 return '' | 398 return '' |
378 cmd = re.sub(r'\$(\d+|\$)', _checkvar, self.definition[1:]) | 399 cmd = re.sub(r'\$(\d+|\$)', _checkvar, self.definition[1:]) |
379 replace = dict((str(i + 1), arg) for i, arg in enumerate(args)) | 400 cmd = aliasinterpolate(self.name, args, cmd) |
380 replace['0'] = self.name | |
381 replace['@'] = ' '.join(args) | |
382 cmd = util.interpolate(r'\$', replace, cmd, escape_prefix=True) | |
383 return util.system(cmd, environ=env, out=ui.fout) | 401 return util.system(cmd, environ=env, out=ui.fout) |
384 self.fn = fn | 402 self.fn = fn |
385 return | 403 return |
386 | 404 |
387 try: | 405 try: |