Skip to content

Commit

Permalink
Implement 'command' to suppress function lookup.
Browse files Browse the repository at this point in the history
Motivated by bash-completion, which does 'command ls'.
  • Loading branch information
Andy Chu committed Sep 23, 2018
1 parent d4676a4 commit 874d1f8
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 11 deletions.
9 changes: 4 additions & 5 deletions core/builtin.py
Original file line number Diff line number Diff line change
Expand Up @@ -884,7 +884,7 @@ def _ResolveNames(names, funcs, path_val):

COMMAND_SPEC = _Register('command')
COMMAND_SPEC.ShortFlag('-v')
COMMAND_SPEC.ShortFlag('-V')
#COMMAND_SPEC.ShortFlag('-V') # Another verbose mode.


def Command(argv, funcs, path_val):
Expand All @@ -897,11 +897,10 @@ def Command(argv, funcs, path_val):
else:
# This is for -v, -V is more detailed.
print(arg)
else:
util.warn('*** command without -v not implemented ***')
status = 1
return status

raise AssertionError('command without -v should have been handled earlier')

return status


TYPE_SPEC = _Register('type')
Expand Down
28 changes: 22 additions & 6 deletions core/cmd_exec.py
Original file line number Diff line number Diff line change
Expand Up @@ -523,7 +523,11 @@ def _MakeProcess(self, node, job_state=None, disable_errexit=False):
p = process.Process(thunk, job_state=job_state)
return p

def _RunSimpleCommand(self, argv, fork_external, span_id):
def _RunSimpleCommand(self, argv, fork_external, span_id, funcs=True):
"""
Args:
fork_external: for subshell ( ls / ) or ( command ls / )
"""
# This happens when you write "$@" but have no arguments.
if not argv:
return 0 # status 0, or skip it?
Expand All @@ -540,13 +544,25 @@ def _RunSimpleCommand(self, argv, fork_external, span_id):
return status

# Builtins like 'true' can be redefined as functions.
func_node = self.funcs.get(arg0)
if func_node is not None:
# NOTE: Functions could call 'exit 42' directly, etc.
status = self._RunFunc(func_node, argv)
return status
if funcs:
func_node = self.funcs.get(arg0)
if func_node is not None:
# NOTE: Functions could call 'exit 42' directly, etc.
status = self._RunFunc(func_node, argv)
return status

builtin_id = builtin.Resolve(arg0)

if builtin_id == builtin_e.COMMAND: # 'command ls' suppresses function lookup
n = len(argv)
if n == 1:
return 0 # 'command', like the 'if not argv' case above
# The 'command' builtin syntax is simple enough that this is 100%
# correct, not a heuristic.
elif n >= 2 and argv[1] != '-v':
return self._RunSimpleCommand(argv[1:], fork_external, span_id,
funcs=False)

if builtin_id != builtin_e.NONE:
try:
status = self._RunBuiltin(builtin_id, argv, span_id)
Expand Down
39 changes: 39 additions & 0 deletions spec/builtins2.test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,42 @@ echo
myfunc
status=1
## END

#### command skips function lookup
seq() {
echo "$@"
}
command # no-op
seq 3
command seq 3
# subshell shouldn't fork another process (but we don't have a good way of
# testing it)
( command seq 3 )
## STDOUT:
3
1
2
3
1
2
3
## END

#### command command seq 3
command command seq 3
## STDOUT:
1
2
3
## END
## N-I zsh stdout-json: ""
## N-I zsh status: 127

#### command command -v seq
seq() {
echo 3
}
command command -v seq
## stdout: seq
## N-I zsh stdout-json: ""
## N-I zsh status: 127

0 comments on commit 874d1f8

Please sign in to comment.