def _Visit(self, node): """ """ #log('VISIT %s', node.__class__.__name__) # NOTE: The tags are not unique!!! We would need this: # if isinstance(node, ast.command) and node.tag == command_e.Simple: # But it's easier to check the __class__ attribute. cls = node.__class__ if cls is command.Simple: #log('SimpleCommand %s', node.words) #log('--') #node.PrettyPrint() # Things to consider: # - source and . # - DONE builtins: get a list from builtin.py # - DONE functions: have to enter function definitions into a dictionary # - Commands that call others: sudo, su, find, xargs, etc. # - builtins that call others: exec, command # - except not command -v! if not node.words: return w = node.words[0] ok, argv0, _ = word_.StaticEval(w) if not ok: log("Couldn't statically evaluate %r", w) return if (builtin.ResolveSpecial(argv0) == builtin_e.NONE and builtin.ResolveAssign(argv0) == builtin_e.NONE and builtin.Resolve(argv0) == builtin_e.NONE): self.progs_used[argv0] = True # NOTE: If argv1 is $0, then we do NOT print a warning! if argv0 == 'sudo': if len(node.words) < 2: return w1 = node.words[1] ok, argv1, _ = word_.StaticEval(w1) if not ok: log("Couldn't statically evaluate %r", w) return # Should we mark them behind 'sudo'? e.g. "sudo apt install"? self.progs_used[argv1] = True elif cls is command.ShFunction: self.funcs_defined[node.name] = True
def RunSimpleCommand(self, arg_vec, fork_external, funcs=True): """Public interface to run a simple command (excluding assignment) Args: fork_external: for subshell ( ls / ) or ( command ls / ) """ argv = arg_vec.strs if arg_vec.spids: span_id = arg_vec.spids[0] else: span_id = const.NO_INTEGER # This happens when you write "$@" but have no arguments. if not argv: if self.exec_opts.strict_argv: e_die("Command evaluated to an empty argv array", span_id=span_id) else: return 0 # status 0, or skip it? arg0 = argv[0] builtin_id = builtin.ResolveAssign(arg0) if builtin_id != builtin_e.NONE: # command readonly is disallowed, for technical reasons. Could relax it # later. self.errfmt.Print("Can't run assignment builtin recursively", span_id=span_id) return 1 builtin_id = builtin.ResolveSpecial(arg0) if builtin_id != builtin_e.NONE: status = self._RunBuiltin(builtin_id, arg_vec, fork_external) # TODO: Enable this and fix spec test failures. # Also update _SPECIAL_BUILTINS in osh/builtin.py. #if status != 0: # e_die('special builtin failed', status=status) return status # Builtins like 'true' can be redefined as functions. 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[1:]) return status builtin_id = builtin.Resolve(arg0) if builtin_id != builtin_e.NONE: return self._RunBuiltin(builtin_id, arg_vec, fork_external) environ = self.mem.GetExported() # Include temporary variables # Resolve argv[0] BEFORE forking. argv0_path = self.search_path.CachedLookup(argv[0]) if argv0_path is None: self.errfmt.Print('%r not found', argv[0], span_id=span_id) return 127 if fork_external: thunk = process.ExternalThunk(self.ext_prog, argv0_path, arg_vec, environ) p = process.Process(thunk, self.job_state) status = p.Run(self.waiter) return status self.ext_prog.Exec(argv0_path, arg_vec, environ) # NEVER RETURNS
def _RunBuiltinAndRaise(self, builtin_id, arg_vec, fork_external): """ Raises: args.UsageError """ # Shift one arg. Builtins don't need to know their own name. argv = arg_vec.strs[1:] # Most builtins dispatch with a dictionary builtin_func = self.builtins.get(builtin_id) if builtin_func is not None: status = builtin_func(arg_vec) # Some builtins "belong" to the executor. elif builtin_id == builtin_e.EXEC: status = self._Exec(arg_vec) # may never return # But if it returns, then we want to permanently apply the redirects # associated with it. self.fd_state.MakePermanent() elif builtin_id == builtin_e.EVAL: status = self._Eval(arg_vec) elif builtin_id in (builtin_e.SOURCE, builtin_e.DOT): status = self._Source(arg_vec) elif builtin_id == builtin_e.COMMAND: # TODO: How do we handle fork_external? It doesn't fit the common # signature. We also don't handle 'command local', etc. b = builtin.Command(self, self.funcs, self.aliases, self.search_path) status = b(arg_vec, fork_external) elif builtin_id == builtin_e.BUILTIN: # NOTE: uses early return style if not argv: return 0 # this could be an error in strict mode? name = arg_vec.strs[1] # Run regular builtin or special builtin to_run = builtin.Resolve(name) if to_run == builtin_e.NONE: to_run = builtin.ResolveSpecial(name) if to_run == builtin_e.NONE: span_id = arg_vec.spids[1] if builtin.ResolveAssign(name) != builtin_e.NONE: # NOTE: There's a similar restriction for 'command' self.errfmt.Print("Can't run assignment builtin recursively", span_id=span_id) else: self.errfmt.Print("%r isn't a shell builtin", span_id=span_id) return 1 arg_vec2 = arg_vector(arg_vec.strs[1:], arg_vec.spids[1:]) status = self._RunBuiltinAndRaise(to_run, arg_vec2, fork_external) else: raise AssertionError('Unhandled builtin: %s' % builtin_id) assert isinstance(status, int) return status