コード例 #1
0
ファイル: cmd_exec.py プロジェクト: harlowja/oil
  def _GetProcessForNode(self, node):
    """
    Assume we will run the node in another process.  Return a process.
    """
    if node.tag == command_e.SimpleCommand:
      words = braces.BraceExpandWords(node.words)
      argv = self.ev.EvalWordSequence(words)
      more_env = self.mem.GetExported()
      self._EvalEnv(node.more_env, more_env)
      thunk = self._GetThunkForSimpleCommand(argv, more_env)

    elif node.tag == command_e.ControlFlow:
      # Pipeline or subshells with control flow are invalid, e.g.:
      # - break | less
      # - continue | less
      # - ( return )
      # NOTE: This could be done at parse time too.
      e_die('Invalid control flow %r in pipeline or subshell', node.token.val,
            token=node.token)

    else:
      thunk = process.SubProgramThunk(self, node)

    redirects = self._EvalRedirects(node)
    p = process.Process(thunk, fd_state=self.fd_state, redirects=redirects)
    return p
コード例 #2
0
ファイル: word_eval.py プロジェクト: jedahan/oil
    def EvalRhsWord(self, word):
        """word_t -> value_t.

    Used for RHS of assignment.  There is no splitting.

    Args:
      ast.word_t

    Returns:
      runtime.value_t
    """
        if word.tag == word_e.EmptyWord:
            return runtime.Str('')

        # Special case for a=(1 2).  ArrayLiteralPart won't appear in words that
        # don't look like assignments.
        if (len(word.parts) == 1
                and word.parts[0].tag == word_part_e.ArrayLiteralPart):

            array_words = word.parts[0].words
            words = braces.BraceExpandWords(array_words)
            strs = self._EvalWordSequence(words)
            #log('ARRAY LITERAL EVALUATED TO -> %s', strs)
            return runtime.StrArray(strs)

        # If RHS doens't look like a=( ... ), then it must be a string.
        return self.EvalWordToString(word)
コード例 #3
0
  def _GetProcessForNode(self, node):
    """
    Assume we will run the node in another process.  Return a process.
    """
    if node.tag == command_e.SimpleCommand:
      words = braces.BraceExpandWords(node.words)
      argv = self.ev.EvalWordSequence(words)
      if argv is None:
        err = self.ev.Error()
        raise AssertionError("Error evaluating words: %s" % err)
      more_env = self.mem.GetExported()
      self._EvalEnv(node.more_env, more_env)
      thunk = self._GetThunkForSimpleCommand(argv, more_env)

    elif node.tag == command_e.ControlFlow:
      # TODO: Raise _FatalError
      # Pipeline or subshells with control flow are invalid, e.g.:
      # - break | less
      # - continue | less
      # - ( return )
      # NOTE: This could be done at parse time too.
      raise AssertionError('Invalid control flow %s' % node)

    else:
      thunk = SubProgramThunk(self, node)

    redirects = self._EvalRedirects(node)
    p = Process(thunk, fd_state=self.fd_state, redirects=redirects)
    return p
コード例 #4
0
    def EvalWordToAny(self, word, glob_escape=False):
        """
    Used for RHS of assignment

    Also used for default value?  e.g. "${a:-"a" "b"}" and so forth.

    Returns:
      arg_value
      Or maybe just string?  Whatever would go in ConstArg and GlobArg.
      But you don't need to distinguish it later.
      You could also have EvalWord and EvalGlobWord methods or EvalPatternWord
    """
        # Special case for a=(1 2).  ArrayLiteralPart won't appear in words that
        # don't look like assignments.
        if (len(word.parts) == 1
                and word.parts[0].tag == word_part_e.ArrayLiteralPart):
            array_words = word.parts[0].words
            words = braces.BraceExpandWords(array_words)
            strs = self._EvalWordSequence(words)
            #log('ARRAY LITERAL EVALUATED TO -> %s', strs)
            return runtime.StrArray(strs)

        part_vals = self._EvalParts(word)
        #log('part_vals %s', part_vals)

        # Instead of splitting, do a trivial transformation to frag array.
        # Example:
        # foo="-$@-${a[@]}-" requires fragment, reframe, and simple join
        frag_arrays = []
        for p in part_vals:
            if p.tag == part_value_e.StringPartValue:
                frag_arrays.append([runtime.fragment(p.s, False, False)])
            elif p.tag == part_value_e.ArrayPartValue:
                frag_arrays.append(
                    [runtime.fragment(s, False, False) for s in p.strs])
            else:
                raise AssertionError

        frag_arrays = _Reframe(frag_arrays)
        #log('frag_arrays %s', frag_arrays)

        # Simple join
        args = []
        for frag_array in frag_arrays:
            args.append(''.join(frag.s for frag in frag_array))

        # Example:
        # a=(1 2)
        # b=$a  # one word
        # c="${a[@]}"  # two words
        if len(args) == 1:
            val = runtime.Str(args[0])
        else:
            # NOTE: For bash compatibility, could have an option to join them here.
            # foo="-$@-${a[@]}-"  -- join with IFS again, like "$*" ?
            # Or maybe do that in cmd_exec in assignment.
            val = runtime.StrArray(args)

        return val
コード例 #5
0
  def _Dispatch(self, node, fork_external):
    # If we call RunCommandSub in a recursive call to the executor, this will
    # be set true (if strict-errexit is false).  But it only lasts for one
    # command.
    self.check_command_sub_status = False

    #argv0 = None  # for error message
    check_errexit = False  # for errexit

    if node.tag == command_e.SimpleCommand:
      check_errexit = True

      # Find span_id for a basic implementation of $LINENO, e.g.
      # PS4='+$SOURCE_NAME:$LINENO:'
      # NOTE: osh2oil uses node.more_env, but we don't need that.
      span_id = const.NO_INTEGER
      if node.words:
        first_word = node.words[0]
        span_id = word.LeftMostSpanForWord(first_word)

      self.mem.SetCurrentSpanId(span_id)

      # PROBLEM: We want to log argv in 'xtrace' mode, but we may have already
      # redirected here, which screws up logging.  For example, 'echo hi
      # >/dev/null 2>&1'.  We want to evaluate argv and log it BEFORE applying
      # redirects.

      # Another problem:
      # - tracing can be called concurrently from multiple processes, leading
      # to overlap.  Maybe have a mode that creates a file per process.
      # xtrace-proc
      # - line numbers for every command would be very nice.  But then you have
      # to print the filename too.

      words = braces.BraceExpandWords(node.words)
      argv = self.word_ev.EvalWordSequence(words)

      # This comes before evaluating env, in case there are problems evaluating
      # it.  We could trace the env separately?  Also trace unevaluated code
      # with set-o verbose?
      self.tracer.OnSimpleCommand(argv)

      if node.more_env:
        self.mem.PushTemp()
      try:
        for env_pair in node.more_env:
          val = self.word_ev.EvalWordToString(env_pair.val)
          # Set each var so the next one can reference it.  Example:
          # FOO=1 BAR=$FOO ls /
          self.mem.SetVar(ast.LhsName(env_pair.name), val,
                          (var_flags_e.Exported,), scope_e.TempEnv)

        # NOTE: This might never return!  In the case of fork_external=False.
        status = self._RunSimpleCommand(argv, fork_external, span_id)
      finally:
        if node.more_env:
          self.mem.PopTemp()

    elif node.tag == command_e.Sentence:
      # Don't check_errexit since this isn't a real node!
      if node.terminator.id == Id.Op_Semi:
        status = self._Execute(node.child)
      else:
        status = self._RunJobInBackground(node.child)

    elif node.tag == command_e.Pipeline:
      check_errexit = True
      if node.stderr_indices:
        raise NotImplementedError('|&')

      if node.negated:
        self._PushErrExit()
        try:
          status2 = self._RunPipeline(node)
        finally:
          self._PopErrExit()

        # errexit is disabled for !.
        check_errexit = False
        status = 1 if status2 == 0 else 0
      else:
        status = self._RunPipeline(node)

    elif node.tag == command_e.Subshell:
      check_errexit = True
      # This makes sure we don't waste a process if we'd launch one anyway.
      p = self._MakeProcess(node.child)
      status = p.Run(self.waiter)

    elif node.tag == command_e.DBracket:
      check_errexit = True
      result = self.bool_ev.Eval(node.expr)
      status = 0 if result else 1

    elif node.tag == command_e.DParen:
      check_errexit = True
      i = self.arith_ev.Eval(node.child)
      status = 0 if i != 0 else 1

    elif node.tag == command_e.Assignment:
      flags = word_compile.ParseAssignFlags(node.flags)

      if node.keyword == Id.Assign_Local:
        lookup_mode = scope_e.LocalOnly
      # typeset and declare are synonyms?  I see typeset -a a=() the most.
      elif node.keyword in (Id.Assign_Declare, Id.Assign_Typeset):
        # declare is like local, except it can also be used outside functions?
        if var_flags_e.Global in flags:
          lookup_mode = scope_e.GlobalOnly
        else:
          lookup_mode = scope_e.LocalOnly
      elif node.keyword == Id.Assign_Readonly:
        lookup_mode = scope_e.Dynamic
        flags.append(var_flags_e.ReadOnly)
      elif node.keyword == Id.Assign_None:  # mutate existing local or global
        lookup_mode = scope_e.Dynamic
      else:
        raise AssertionError(node.keyword)

      for pair in node.pairs:
        if pair.op == assign_op_e.PlusEqual:
          assert pair.rhs, pair.rhs  # I don't think a+= is valid?
          val = self.word_ev.EvalRhsWord(pair.rhs)
          old_val, lval = expr_eval.EvalLhsAndLookup(pair.lhs, self.arith_ev,
                                                     self.mem, self.exec_opts)
          sig = (old_val.tag, val.tag)
          if sig == (value_e.Undef, value_e.Str):
            pass  # val is RHS
          elif sig == (value_e.Undef, value_e.StrArray):
            pass  # val is RHS
          elif sig == (value_e.Str, value_e.Str):
            val = runtime.Str(old_val.s + val.s)
          elif sig == (value_e.Str, value_e.StrArray):
            e_die("Can't append array to string")
          elif sig == (value_e.StrArray, value_e.Str):
            e_die("Can't append string to array")
          elif sig == (value_e.StrArray, value_e.StrArray):
            val = runtime.StrArray(old_val.strs + val.strs)

        else:  # plain assignment
          spid = pair.spids[0]  # Source location for tracing
          lval = self._EvalLhs(pair.lhs, spid, lookup_mode)

          # RHS can be a string or array.
          if pair.rhs:
            val = self.word_ev.EvalRhsWord(pair.rhs)
            assert isinstance(val, runtime.value), val

          else:  # e.g. 'readonly x' or 'local x'
            val = None

        # NOTE: In bash and mksh, declare -a myarray makes an empty cell with
        # Undef value, but the 'array' attribute.

        #log('setting %s to %s with flags %s', lval, val, flags)
        self.mem.SetVar(lval, val, flags, lookup_mode,
                        strict_array=self.exec_opts.strict_array)

        # Assignment always appears to have a spid.
        if node.spids:
          current_spid = node.spids[0]
        else:
          current_spid = const.NO_INTEGER
        self.mem.SetCurrentSpanId(current_spid)
        self.tracer.OnAssignment(lval, pair.op, val, flags, lookup_mode)

      # PATCH to be compatible with existing shells: If the assignment had a
      # command sub like:
      #
      # s=$(echo one; false)
      #
      # then its status will be in mem.last_status, and we can check it here.
      # If there was NOT a command sub in the assignment, then we don't want to
      # check it.
      if node.keyword == Id.Assign_None:  # mutate existing local or global
        # Only do this if there was a command sub?  How?  Look at node?
        # Set a flag in mem?   self.mem.last_status or
        if self.check_command_sub_status:
          self._CheckStatus(self.mem.last_status, node)
          # A global assignment shouldn't clear $?.
          status = self.mem.last_status
        else:
          status = 0
      else:
        # To be compatible with existing shells, local assignments DO clear
        # $?.  Even in strict mode, we don't need to bother setting
        # check_errexit = True, because we would have already checked the
        # command sub in RunCommandSub.
        status = 0
        # TODO: maybe we should have a "sane-status" that respects this:
        # false; echo $?; local f=x; echo $?

    elif node.tag == command_e.ControlFlow:
      if node.arg_word:  # Evaluate the argument
        val = self.word_ev.EvalWordToString(node.arg_word)
        assert val.tag == value_e.Str
        arg = int(val.s)  # They all take integers
      else:
        arg = 0  # return 0, exit 0, break 0 levels, etc.

      # NOTE: We don't do anything about a top-level 'return' here.  Unlike in
      # bash, that is OK.  If you can return from a sourced script, it makes
      # sense to return from a main script.
      ok = True
      tok = node.token
      if (tok.id in (Id.ControlFlow_Break, Id.ControlFlow_Continue) and
          self.loop_level == 0):
        ok = False
        msg = 'Invalid control flow at top level'

      if ok:
        raise _ControlFlow(tok, arg)

      if self.exec_opts.strict_control_flow:
        e_die(msg, token=tok)
      else:
        # Only print warnings, never fatal.
        # Bash oddly only exits 1 for 'return', but no other shell does.
        ui.PrintFilenameAndLine(tok.span_id, self.arena)
        util.warn(msg)
        status = 0

    # The only difference between these two is that CommandList has no
    # redirects.  We already took care of that above.
    elif node.tag in (command_e.CommandList, command_e.BraceGroup):
      status = self._ExecuteList(node.children)
      check_errexit = False

    elif node.tag == command_e.AndOr:
      # NOTE: && and || have EQUAL precedence in command mode.  See case #13
      # in dbracket.test.sh.

      left = node.children[0]

      # Suppress failure for every child except the last one.
      self._PushErrExit()
      try:
        status = self._Execute(left)
      finally:
        self._PopErrExit()

      i = 1
      n = len(node.children)
      while i < n:
        #log('i %d status %d', i, status)
        child = node.children[i]
        op_id = node.ops[i-1]

        #log('child %s op_id %s', child, op_id)

        if op_id == Id.Op_DPipe and status == 0:
          i += 1
          continue  # short circuit

        elif op_id == Id.Op_DAmp and status != 0:
          i += 1
          continue  # short circuit

        if i == n - 1:  # errexit handled differently for last child
          status = self._Execute(child)
          check_errexit = True
        else:
          self._PushErrExit()
          try:
            status = self._Execute(child)
          finally:
            self._PopErrExit()

        i += 1

    elif node.tag == command_e.WhileUntil:
      if node.keyword.id == Id.KW_While:
        _DonePredicate = lambda status: status != 0
      else:
        _DonePredicate = lambda status: status == 0

      status = 0

      self.loop_level += 1
      try:
        while True:
          self._PushErrExit()
          try:
            cond_status = self._ExecuteList(node.cond)
          finally:
            self._PopErrExit()

          done = cond_status != 0
          if _DonePredicate(cond_status):
            break
          try:
            status = self._Execute(node.body)  # last one wins
          except _ControlFlow as e:
            if e.IsBreak():
              status = 0
              break
            elif e.IsContinue():
              status = 0
              continue
            else:  # return needs to pop up more
              raise
      finally:
        self.loop_level -= 1

    elif node.tag == command_e.ForEach:
      iter_name = node.iter_name
      if node.do_arg_iter:
        iter_list = self.mem.GetArgv()
      else:
        words = braces.BraceExpandWords(node.iter_words)
        iter_list = self.word_ev.EvalWordSequence(words)
        # We need word splitting and so forth
        # NOTE: This expands globs too.  TODO: We should pass in a Globber()
        # object.

      status = 0  # in case we don't loop
      self.loop_level += 1
      try:
        for x in iter_list:
          #log('> ForEach setting %r', x)
          state.SetLocalString(self.mem, iter_name, x)
          #log('<')

          try:
            status = self._Execute(node.body)  # last one wins
          except _ControlFlow as e:
            if e.IsBreak():
              status = 0
              break
            elif e.IsContinue():
              status = 0
            else:  # return needs to pop up more
              raise
      finally:
        self.loop_level -= 1

    elif node.tag == command_e.ForExpr:
      status = 0
      init, cond, body, update = node.init, node.cond, node.body, node.update
      if init:
        self.arith_ev.Eval(init)

      self.loop_level += 1
      try:
        while True:
          if cond:
            b = self.arith_ev.Eval(cond)
            if not b:
              break

          try:
            status = self._Execute(body)
          except _ControlFlow as e:
            if e.IsBreak():
              status = 0
              break
            elif e.IsContinue():
              status = 0
            else:  # return needs to pop up more
              raise

          if update:
            self.arith_ev.Eval(update)

      finally:
        self.loop_level -= 1

    elif node.tag == command_e.DoGroup:
      status = self._ExecuteList(node.children)
      check_errexit = False  # not real statements

    elif node.tag == command_e.FuncDef:
      # NOTE: Would it make sense to evaluate the redirects BEFORE entering?
      # It will save time on function calls.
      self.funcs[node.name] = node
      status = 0

    elif node.tag == command_e.If:
      done = False
      for arm in node.arms:
        self._PushErrExit()
        try:
          status = self._ExecuteList(arm.cond)
        finally:
          self._PopErrExit()

        if status == 0:
          status = self._ExecuteList(arm.action)
          done = True
          break
      # TODO: The compiler should flatten this
      if not done and node.else_action is not None:
        status = self._ExecuteList(node.else_action)

    elif node.tag == command_e.NoOp:
      status = 0  # make it true

    elif node.tag == command_e.Case:
      val = self.word_ev.EvalWordToString(node.to_match)
      to_match = val.s

      status = 0  # If there are no arms, it should be zero?
      done = False

      for arm in node.arms:
        for pat_word in arm.pat_list:
          # NOTE: Is it OK that we're evaluating these as we go?

          # TODO: case "$@") shouldn't succeed?  That's a type error?
          # That requires strict-array?

          pat_val = self.word_ev.EvalWordToString(pat_word, do_fnmatch=True)
          #log('Matching word %r against pattern %r', to_match, pat_val.s)
          if libc.fnmatch(pat_val.s, to_match):
            status = self._ExecuteList(arm.action)
            done = True  # TODO: Parse ;;& and for fallthrough and such?
            break  # Only execute action ONCE
        if done:
          break

    elif node.tag == command_e.TimeBlock:
      # TODO:
      # - When do we need RUSAGE_CHILDREN?
      # - Respect TIMEFORMAT environment variable.
      # "If this variable is not set, Bash acts as if it had the value"
      # $'\nreal\t%3lR\nuser\t%3lU\nsys\t%3lS'
      # "A trailing newline is added when the format string is displayed."

      start_t = time.time()  # calls gettimeofday() under the hood
      start_u = resource.getrusage(resource.RUSAGE_SELF)
      status = self._Execute(node.pipeline)

      end_t = time.time()
      end_u = resource.getrusage(resource.RUSAGE_SELF)

      real = end_t - start_t
      user = end_u.ru_utime - start_u.ru_utime
      sys_ = end_u.ru_stime - start_u.ru_stime
      libc.print_time(real, user, sys_)

    else:
      raise NotImplementedError(node.__class__.__name__)

    return status, check_errexit
コード例 #6
0
ファイル: cmd_exec.py プロジェクト: neuroradiology/oil
  def _Dispatch(self, node, fork_external):
    argv0 = None  # for error message
    check_errexit = True  # for errexit

    if node.tag == command_e.SimpleCommand:
      # PROBLEM: We want to log argv in 'xtrace' mode, but we may have already
      # redirected here, which screws up loggnig.  For example, 'echo hi
      # >/dev/null 2>&1'.  We want to evaluate argv and log it BEFORE applying
      # redirects.

      # Another problem:
      # - tracing can be called concurrently from multiple processes, leading
      # to overlap.  Maybe have a mode that creates a file per process.
      # xtrace-proc
      # - line numbers for every command would be very nice.  But then you have
      # to print the filename too.

      words = braces.BraceExpandWords(node.words)
      argv = self.ev.EvalWordSequence(words)
      if argv:
        argv0 = argv[0]

      environ = self.mem.GetExported()
      self._EvalEnv(node.more_env, environ)

      if self.exec_opts.xtrace:
        log('+ %s', argv)
        #print('+ %s' % argv, file=sys.stderr)
        #print('+ %s' % argv, file=self.XFILE)
        #os.write(2, '+ %s\n' % argv)

      status = self._RunSimpleCommand(argv, environ, fork_external)

      if self.exec_opts.xtrace:
        #log('+ %s -> %d', argv, status)
        pass

    elif node.tag == command_e.Sentence:
      if node.terminator.id == Id.Op_Semi:
        # Don't check_errexit since this isn't a real node!
        check_errexit = False
        status = self._Execute(node.child)
      else:
        status = self._RunJobInBackground(node.child)

    elif node.tag == command_e.Pipeline:
      if node.stderr_indices:
        raise NotImplementedError('|&')

      if node.negated:
        self._PushErrExit()
        try:
          status2 = self._RunPipeline(node)
        finally:
          self._PopErrExit()

        # errexit is disabled for !.
        check_errexit = False
        status = 1 if status2 == 0 else 0
      else:
        status = self._RunPipeline(node)

    elif node.tag == command_e.Subshell:
      # This makes sure we don't waste a process if we'd launch one anyway.
      p = self._MakeProcess(node.child)
      status = p.Run(self.waiter)

    elif node.tag == command_e.DBracket:
      result = self.bool_ev.Eval(node.expr)
      status = 0 if result else 1

    elif node.tag == command_e.DParen:
      i = self.arith_ev.Eval(node.child)
      status = 0 if i != 0 else 1

    elif node.tag == command_e.Assignment:
      pairs = []
      if node.keyword == Id.Assign_Local:
        lookup_mode = scope.LocalOnly
        flags = ()
      elif node.keyword == Id.Assign_Declare:
        # declare is like local, except it can also be used outside functions?
        lookup_mode = scope.LocalOnly
        # TODO: Respect flags.  -r and -x matter, but -a and -A might be
        # implicit in the RHS?
        flags = ()
      elif node.keyword == Id.Assign_Readonly:
        lookup_mode = scope.Dynamic
        flags = (var_flags.ReadOnly,)
      elif node.keyword == Id.Assign_None:  # mutate existing local or global
        lookup_mode = scope.Dynamic
        flags = ()
      else:
        # TODO: typeset, declare, etc.  Those are dynamic though.
        raise NotImplementedError(node.keyword)

      for pair in node.pairs:
        if pair.rhs:
          # RHS can be a string or array.
          val = self.ev.EvalWordToAny(pair.rhs)
          assert isinstance(val, runtime.value), val
        else:
          # 'local x' is equivalent to local x=""
          val = runtime.Str('')

        if pair.op == assign_op.PlusEqual:
          old_val, lval = expr_eval.EvalLhs(pair.lhs, self.arith_ev, self.mem,
                                            self.exec_opts)
          sig = (old_val.tag, val.tag)
          if sig == (value_e.Str, value_e.Str):
            val = runtime.Str(old_val.s + val.s)
          elif sig == (value_e.Str, value_e.StrArray):
            e_die("Can't append array to string")
          elif sig == (value_e.StrArray, value_e.Str):
            e_die("Can't append string to array")
          elif sig == (value_e.StrArray, value_e.StrArray):
            val = runtime.StrArray(old_val.strs + val.strs)
        else:
          lval = self._EvalLhs(pair.lhs)

        #log('ASSIGNING %s -> %s', lval, val)
        self.mem.SetVar(lval, val, flags, lookup_mode)

      # TODO: This should be eval of RHS, unlike bash!
      status = 0

    elif node.tag == command_e.ControlFlow:
      if node.arg_word:  # Evaluate the argument
        val = self.ev.EvalWordToString(node.arg_word)
        assert val.tag == value_e.Str
        arg = int(val.s)  # They all take integers
      else:
        arg = 0  # return 0, break 0 levels, etc.

      # NOTE: always raises so we don't set status.
      raise _ControlFlow(node.token, arg)

    # The only difference between these two is that CommandList has no
    # redirects.  We already took care of that above.
    elif node.tag in (command_e.CommandList, command_e.BraceGroup):
      status = self._ExecuteList(node.children)

    elif node.tag == command_e.AndOr:
      #print(node.children)
      left, right = node.children

      # This is everything except the last one.
      self._PushErrExit()
      try:
        status = self._Execute(left)
      finally:
        self._PopErrExit()

      if node.op_id == Id.Op_DPipe:
        if status != 0:
          status = self._Execute(right)
      elif node.op_id == Id.Op_DAmp:
        if status == 0:
          status = self._Execute(right)
      else:
        raise AssertionError

    elif node.tag in (command_e.While, command_e.Until):
      # TODO: Compile this out?
      if node.tag == command_e.While:
        _DonePredicate = lambda status: status != 0
      else:
        _DonePredicate = lambda status: status == 0

      status = 0
      while True:
        self._PushErrExit()
        try:
          cond_status = self._ExecuteList(node.cond)
        finally:
          self._PopErrExit()

        done = cond_status != 0
        if _DonePredicate(cond_status):
          break
        try:
          status = self._Execute(node.body)  # last one wins
        except _ControlFlow as e:
          if e.IsBreak():
            status = 0
            break
          elif e.IsContinue():
            status = 0
            continue
          else:  # return needs to pop up more
            raise

    elif node.tag == command_e.ForEach:
      iter_name = node.iter_name
      if node.do_arg_iter:
        iter_list = self.mem.GetArgv()
      else:
        words = braces.BraceExpandWords(node.iter_words)
        iter_list = self.ev.EvalWordSequence(words)
        # We need word splitting and so forth
        # NOTE: This expands globs too.  TODO: We should pass in a Globber()
        # object.
      status = 0  # in case we don't loop
      for x in iter_list:
        #log('> ForEach setting %r', x)
        state.SetLocalString(self.mem, iter_name, x)
        #log('<')

        try:
          status = self._Execute(node.body)  # last one wins
        except _ControlFlow as e:
          if e.IsBreak():
            status = 0
            break
          elif e.IsContinue():
            status = 0
            continue
          else:  # return needs to pop up more
            raise

    elif node.tag == command_e.ForExpr:
      raise NotImplementedError(node.tag)

    elif node.tag == command_e.DoGroup:
      status = self._ExecuteList(node.children)

    elif node.tag == command_e.FuncDef:
      # NOTE: Would it make sense to evaluate the redirects BEFORE entering?
      # It will save time on function calls.
      self.funcs[node.name] = node
      status = 0

    elif node.tag == command_e.If:
      done = False
      for arm in node.arms:
        self._PushErrExit()
        try:
          status = self._ExecuteList(arm.cond)
        finally:
          self._PopErrExit()

        if status == 0:
          status = self._ExecuteList(arm.action)
          done = True
          break
      # TODO: The compiler should flatten this
      if not done and node.else_action is not None:
        status = self._ExecuteList(node.else_action)

    elif node.tag == command_e.NoOp:
      status = 0  # make it true

    elif node.tag == command_e.Case:
      val = self.ev.EvalWordToString(node.to_match)
      to_match = val.s

      status = 0  # If there are no arms, it should be zero?
      done = False

      for arm in node.arms:
        for pat_word in arm.pat_list:
          # NOTE: Is it OK that we're evaluating these as we go?
          pat_val = self.ev.EvalWordToString(pat_word, do_fnmatch=True)
          #log('Matching word %r against pattern %r', to_match, pat_val.s)
          if libc.fnmatch(pat_val.s, to_match):
            status = self._ExecuteList(arm.action)
            done = True  # TODO: Parse ;;& and for fallthrough and such?
        if done:
          break

    elif node.tag == command_e.TimeBlock:
      # TODO:
      # - When do we need RUSAGE_CHILDREN?
      # - Respect TIMEFORMAT environment variable.
      # "If this variable is not set, Bash acts as if it had the value"
      # $'\nreal\t%3lR\nuser\t%3lU\nsys\t%3lS'
      # "A trailing newline is added when the format string is displayed."

      start_t = time.time()  # calls gettimeofday() under the hood
      start_u = resource.getrusage(resource.RUSAGE_SELF)
      status = self._Execute(node.pipeline)
      end_t = time.time()
      end_u = resource.getrusage(resource.RUSAGE_SELF)

      real = end_t - start_t
      user = end_u.ru_utime - start_u.ru_utime
      sys_ = end_u.ru_stime - start_u.ru_stime
      print('real\t%.3f' % real, file=sys.stderr)
      print('user\t%.3f' % user, file=sys.stderr)
      print('sys\t%.3f' % sys_, file=sys.stderr)

    else:
      raise AssertionError(node.tag)

    return status, check_errexit
コード例 #7
0
ファイル: cmd_exec.py プロジェクト: harlowja/oil
  def _Execute(self, node):
    """
    Args:
      node: of type AstNode
    """
    redirects = self._EvalRedirects(node)

    # TODO: Only eval argv[0] once.  It can have side effects!
    if node.tag == command_e.SimpleCommand:
      words = braces.BraceExpandWords(node.words)
      argv = self.ev.EvalWordSequence(words)

      more_env = self.mem.GetExported()
      self._EvalEnv(node.more_env, more_env)
      thunk = self._GetThunkForSimpleCommand(argv, more_env)

      # Don't waste a process if we'd launch one anyway.
      if thunk.IsExternal():
        p = process.Process(thunk, fd_state=self.fd_state, redirects=redirects)
        status = p.Run()

      else:  # Internal
        #log('ARGV %s', argv)

        # NOTE: _EvalRedirects turns LST nodes into core/process.py nodes.  And
        # then we use polymorphism here.  Does it make sense to use functional
        # style based on the RedirType?  Might be easier to read.

        self.fd_state.PushFrame()
        for r in redirects:
          r.ApplyInParent(self.fd_state)

        status = thunk.RunInParent()
        restore_fd_state = thunk.ShouldRestoreFdState()

        # Special case for exec 1>&2 (with no args): we permanently change the
        # fd state.  BUT we don't want to restore later.
        # TODO: Instead of this, maybe r.ApplyPermaent(self.fd_state)?
        if restore_fd_state:
          self.fd_state.PopAndRestore()
        else:
          self.fd_state.PopAndForget()

    elif node.tag == command_e.Sentence:
      # TODO: Compile this away.
      status = self._Execute(node.command)

    elif node.tag == command_e.Pipeline:
      status = self._RunPipeline(node)

    elif node.tag == command_e.Subshell:
      # This makes sure we don't waste a process if we'd launch one anyway.
      p = self._GetProcessForNode(node.children[0])
      status = p.Run()

    elif node.tag == command_e.DBracket:
      bool_ev = expr_eval.BoolEvaluator(self.mem, self.ev)
      ok = bool_ev.Eval(node.expr)
      if ok:
        status = 0 if bool_ev.Result() else 1
      else:
        e_die('Error evaluating boolean: %s' % bool_ev.Error())

    elif node.tag == command_e.DParen:
      arith_ev = expr_eval.ArithEvaluator(self.mem, self.ev)
      ok = arith_ev.Eval(node.child)
      if ok:
        i = arith_ev.Result()
        # Negate the value: non-zero in arithmetic is true, which is zero in
        # shell land
        status = 0 if i != 0 else 1
      else:
        e_die('Error evaluating (( )): %s' % arith_ev.Error())

    elif node.tag == command_e.Assignment:
      pairs = []
      for pair in node.pairs:
        if pair.rhs:
          # RHS can be a string or array.
          val = self.ev.EvalWordToAny(pair.rhs)
          assert isinstance(val, runtime.value), val
        else:
          # 'local x' is equivalent to local x=""
          val = runtime.Str('')
        pairs.append((pair.lhs, val))

      if node.keyword == Id.Assign_Local:
        self.mem.SetLocals(pairs)
      else:
        # NOTE: could be readonly/export/etc.
        self.mem.SetLocalsOrGlobals(pairs)

      # TODO: This should be eval of RHS, unlike bash!
      status = 0

    elif node.tag == command_e.ControlFlow:
      if node.arg_word:  # Evaluate the argument
        _, val = self.ev.EvalWordToString(node.arg_word)
        assert val.tag == value_e.Str
        arg = int(val.s)  # They all take integers
      else:
        arg = 0  # return 0, break 0 levels, etc.

      raise _ControlFlow(node.token, arg)

    # The only difference between these two is that CommandList has no
    # redirects.  We already took care of that above.
    elif node.tag in (command_e.CommandList, command_e.BraceGroup):
      self.fd_state.PushFrame()
      for r in redirects:
        r.ApplyInParent(self.fd_state)

      status = 0  # for empty list
      for child in node.children:
        status = self._Execute(child)  # last status wins

      self.fd_state.PopAndRestore()

    elif node.tag == command_e.AndOr:
      #print(node.children)
      left, right = node.children
      status = self._Execute(left)

      if node.op_id == Id.Op_DPipe:
        if status != 0:
          status = self._Execute(right)
      elif node.op_id == Id.Op_DAmp:
        if status == 0:
          status = self._Execute(right)
      else:
        raise AssertionError

    elif node.tag in (command_e.While, command_e.Until):
      # TODO: Compile this out?
      if node.tag == command_e.While:
        _DonePredicate = lambda status: status != 0
      else:
        _DonePredicate = lambda status: status == 0

      while True:
        status = self._Execute(node.cond)
        done = status != 0
        if _DonePredicate(status):
          break
        try:
          status = self._Execute(node.body)  # last one wins
        except _ControlFlow as e:
          if e.IsBreak():
            status = 0
            break
          elif e.IsContinue():
            status = 0
            continue
          else:  # return needs to pop up more
            raise

    elif node.tag == command_e.ForEach:
      iter_name = node.iter_name
      if node.do_arg_iter:
        iter_list = self.mem.GetArgv()
      else:
        words = braces.BraceExpandWords(node.iter_words)
        iter_list = self.ev.EvalWordSequence(words)
        # We need word splitting and so forth
        # NOTE: This expands globs too.  TODO: We should pass in a Globber()
        # object.
      status = 0  # in case we don't loop
      for x in iter_list:
        #log('> ForEach setting %r', x)
        self.mem.SetLocal(iter_name, runtime.Str(x))
        #log('<')

        try:
          status = self._Execute(node.body)  # last one wins
        except _ControlFlow as e:
          if e.IsBreak():
            status = 0
            break
          elif e.IsContinue():
            status = 0
            continue
          else:  # return needs to pop up more
            raise

    elif node.tag == command_e.ForExpr:
      raise NotImplementedError(node.tag)

    elif node.tag == command_e.DoGroup:
      # Delegate to command list
      # TODO: This should be compiled out!
      status = self._Execute(node.child)

    elif node.tag == command_e.FuncDef:
      self.funcs[node.name] = node
      status = 0

    elif node.tag == command_e.If:
      done = False
      for arm in node.arms:
        status = self._Execute(arm.cond)
        if status == 0:
          status = self._Execute(arm.action)
          done = True
          break
      # TODO: The compiler should flatten this
      if not done and node.else_action is not None:
        status = self._Execute(node.else_action)

    elif node.tag == command_e.NoOp:
      status = 0  # make it true

    elif node.tag == command_e.Case:
      ok, val = self.ev.EvalWordToString(node.to_match)
      assert ok
      to_match = val.s

      status = 0  # If there are no arms, it should be zero?
      done = False

      for arm in node.arms:
        for pat_word in arm.pat_list:
          # NOTE: Is it OK that we're evaluating these as we go?
          ok, pat_val = self.ev.EvalWordToString(pat_word, do_fnmatch=True)
          assert ok
          #log('Matching word %r against pattern %r', to_match, pat_val.s)
          if libc.fnmatch(pat_val.s, to_match):
            status = self._Execute(arm.action)
            done = True  # TODO: Parse ;;& and for fallthrough and such?
        if done:
          break

    elif node.tag == command_e.TimeBlock:
      # TODO:
      # - When do we need RUSAGE_CHILDREN?
      # - Respect TIMEFORMAT environment variable.
      # "If this variable is not set, Bash acts as if it had the value"
      # $'\nreal\t%3lR\nuser\t%3lU\nsys\t%3lS'
      # "A trailing newline is added when the format string is displayed."

      start_t = time.time()  # calls gettimeofday() under the hood
      start_u = resource.getrusage(resource.RUSAGE_SELF)
      status = self._Execute(node.pipeline)
      end_t = time.time()
      end_u = resource.getrusage(resource.RUSAGE_SELF)

      real = end_t - start_t
      user = end_u.ru_utime - start_u.ru_utime
      sys_ = end_u.ru_stime - start_u.ru_stime
      print('real\t%.3f' % real, file=sys.stderr)
      print('user\t%.3f' % user, file=sys.stderr)
      print('sys\t%.3f' % sys_, file=sys.stderr)

    else:
      raise AssertionError(node.tag)

    if self.exec_opts.errexit and status != 0:
      if node.tag == command_e.SimpleCommand:
        # TODO: Add context
        e_die('%r command exited with status %d (%s)', argv[0], status, node.words[0])
      else:
        e_die('%r command exited with status %d', node.__class__.__name__, status)

    # TODO: Is this the right place to put it?  Does it need a stack for
    # function calls?
    self.mem.last_status = status
    return status
コード例 #8
0
  def _Execute(self, node):
    """
    Args:
      node: of type AstNode
    """
    redirects = self._EvalRedirects(node)

    # TODO: Only eval argv[0] once.  It can have side effects!
    if node.tag == command_e.SimpleCommand:
      words = braces.BraceExpandWords(node.words)
      argv = self.ev.EvalWordSequence(words)

      if argv is None:
        self.error_stack.extend(self.ev.Error())
        raise _FatalError()
      more_env = self.mem.GetExported()
      self._EvalEnv(node.more_env, more_env)
      thunk = self._GetThunkForSimpleCommand(argv, more_env)

      # Don't waste a process if we'd launch one anyway.
      if thunk.IsExternal():
        p = Process(thunk, fd_state=self.fd_state, redirects=redirects)
        status = p.Run()

      else:  # Internal
        #log('ARGV %s', argv)

        # NOTE: _EvalRedirects turns LST nodes into core/process.py nodes.  And
        # then we use polymorphism here.  Does it make sense to use functional
        # style based on the RedirType?  Might be easier to read.

        self.fd_state.PushFrame()
        for r in redirects:
          r.ApplyInParent(self.fd_state)

        status = thunk.RunInParent()
        restore_fd_state = thunk.ShouldRestoreFdState()

        # Special case for exec 1>&2 (with no args): we permanently change the
        # fd state.  BUT we don't want to restore later.
        # TODO: Instead of this, maybe r.ApplyPermaent(self.fd_state)?
        if restore_fd_state:
          self.fd_state.PopAndRestore()
        else:
          self.fd_state.PopAndForget()

    elif node.tag == command_e.Sentence:
      # TODO: Compile this away.
      status = self._Execute(node.command)

    elif node.tag == command_e.Pipeline:
      status = self._RunPipeline(node)

    elif node.tag == command_e.Subshell:
      # This makes sure we don't waste a process if we'd launch one anyway.
      p = self._GetProcessForNode(node.children[0])
      status = p.Run()

    elif node.tag == command_e.DBracket:
      bool_ev = expr_eval.BoolEvaluator(self.mem, self.ev)
      ok = bool_ev.Eval(node.expr)
      if ok:
        status = 0 if bool_ev.Result() else 1
      else:
        raise AssertionError('Error evaluating boolean: %s' % bool_ev.Error())

    elif node.tag == command_e.DParen:
      arith_ev = expr_eval.ArithEvaluator(self.mem, self.ev)
      ok = arith_ev.Eval(node.child)
      if ok:
        i = arith_ev.Result()
        # Negate the value: non-zero in arithmetic is true, which is zero in
        # shell land
        status = 0 if i != 0 else 1
      else:
        raise AssertionError('Error evaluating (( )): %s' % arith_ev.Error())

    elif node.tag == command_e.Assignment:
      pairs = []
      for pair in node.pairs:
        # RHS can be a string or array.
        ok, val = self.ev.EvalWordToAny(pair.rhs)
        assert isinstance(val, runtime.value), val
        #log('RHS %s -> %s', pair.rhs, val)
        if not ok:
          self.error_stack.extend(self.ev.Error())
          raise _FatalError()
        pairs.append((pair.lhs, val))

      if node.keyword == Id.Assign_Local:
        self.mem.SetLocals(pairs)
      else:  # could be readonly/export/etc.
        self.mem.SetGlobals(pairs)

      # TODO: This should be eval of RHS, unlike bash!
      status = 0

    elif node.tag == command_e.ControlFlow:
      if node.arg_word:  # Evaluate the argument
        ok, val = self.ev.EvalWordToString(node.arg_word)
        if not ok:
          self.error_stack.extend(self.ev.Error())
          raise _FatalError()
        assert val.tag == value_e.Str
        arg = int(val.s)  # They all take integers
      else:
        arg = 0  # return 0, break 0 levels, etc.

      raise _ControlFlow(node.token, arg)

    # The only difference between these two is that CommandList has no
    # redirects.  We already took care of that above.
    elif node.tag in (command_e.CommandList, command_e.BraceGroup):
      status = 0  # for empty list
      for child in node.children:
        status = self._Execute(child)  # last status wins

    elif node.tag == command_e.AndOr:
      #print(node.children)
      left, right = node.children
      status = self._Execute(left)

      if node.op_id == Id.Op_DPipe:
        if status != 0:
          status = self._Execute(right)
      elif node.op_id == Id.Op_DAmp:
        if status == 0:
          status = self._Execute(right)
      else:
        raise AssertionError

    elif node.tag in (command_e.While, command_e.Until):
      # TODO: Compile this out?
      if node.tag == command_e.While:
        _DonePredicate = lambda status: status != 0
      else:
        _DonePredicate = lambda status: status == 0

      while True:
        status = self._Execute(node.cond)
        done = status != 0
        if _DonePredicate(status):
          break
        try:
          status = self._Execute(node.body)  # last one wins
        except _ControlFlow as e:
          if e.IsBreak():
            status = 0
            break
          elif e.IsContinue():
            status = 0
            continue
          else:  # return needs to pop up more
            raise

    elif node.tag == command_e.ForEach:
      iter_name = node.iter_name
      if node.do_arg_iter:
        iter_list = self.mem.GetArgv()
      else:
        words = braces.BraceExpandWords(node.iter_words)
        iter_list = self.ev.EvalWordSequence(words)
        # We need word splitting and so forth
        # NOTE: This expands globs too.  TODO: We should pass in a Globber()
        # object.
      status = 0  # in case we don't loop
      for x in iter_list:
        #log('> ForEach setting %r', x)
        self.mem.SetLocal(iter_name, runtime.Str(x))
        #log('<')

        try:
          status = self._Execute(node.body)  # last one wins
        except _ControlFlow as e:
          if e.IsBreak():
            status = 0
            break
          elif e.IsContinue():
            status = 0
            continue
          else:  # return needs to pop up more
            raise

    elif node.tag == command_e.ForExpr:
      raise NotImplementedError(node.tag)

    elif node.tag == command_e.DoGroup:
      # Delegate to command list
      # TODO: This should be compiled out!
      status = self._Execute(node.child)

    elif node.tag == command_e.FuncDef:
      self.funcs[node.name] = node
      status = 0

    elif node.tag == command_e.If:
      done = False
      for arm in node.arms:
        status = self._Execute(arm.cond)
        if status == 0:
          status = self._Execute(arm.action)
          done = True
          break
      # TODO: The compiler should flatten this
      if not done and node.else_action is not None:
        status = self._Execute(node.else_action)

    elif node.tag == command_e.NoOp:
      status = 0  # make it true

    elif node.tag == command_e.Case:
      ok, val = self.ev.EvalWordToString(node.to_match)
      assert ok
      to_match = val.s

      status = 0  # If there are no arms, it should be zero?
      done = False
      for arm in node.arms:
        for pat_word in arm.pat_list:
          # NOTE: Is it OK that we're evaluating these as we go?
          ok, pat_val = self.ev.EvalWordToString(pat_word, do_fnmatch=True)
          assert ok
          #log('Matching word %r against pattern %r', to_match, pat_val.s)
          if libc.fnmatch(pat_val.s, to_match):
            status = self._Execute(arm.action)
            done = True  # TODO: Parse ;;& and for fallthrough and such?
        if done:
          break

    else:
      raise AssertionError(node.tag)

    if self.exec_opts.errexit:
      if status != 0:
        # TODO: token should be set to what?  Is it node.begin_word and
        # node.end_word?
        token = None
        tb = self.mem.GetTraceback(token)
        self._SetException(tb,
            "Command %s exited with code %d" % ('TODO', status))
        # TODO: raise _ControlFlow?  Except?
        # Dummy?

    # TODO: Is this the right place to put it?  Does it need a stack for
    # function calls?
    self.mem.last_status = status
    return status