Esempio n. 1
0
    def RunCommandSub(self, node):
        # type: (command_t) -> str

        # Hack for weird $(<file) construct
        if node.tag_() == command_e.Simple:
            simple = cast(command__Simple, node)
            # Detect '< file'
            if (len(simple.words) == 0 and len(simple.redirects) == 1
                    and simple.redirects[0].op.id == Id.Redir_Less):
                # change it to __cat < file
                # note: cmd_eval.py _Dispatch works around lack of spid
                tok = Token(Id.Lit_Chars, runtime.NO_SPID, '__cat')
                cat_word = compound_word([tok])
                # MUTATE the command.Simple node.  This will only be done the first
                # time in the parent process.
                simple.words.append(cat_word)

        p = self._MakeProcess(node,
                              inherit_errexit=self.exec_opts.inherit_errexit())

        r, w = posix.pipe()
        p.AddStateChange(process.StdoutToPipe(r, w))
        _ = p.Start()
        #log('Command sub started %d', pid)

        chunks = []  # type: List[str]
        posix.close(w)  # not going to write
        while True:
            byte_str = posix.read(r, 4096)
            if len(byte_str) == 0:
                break
            chunks.append(byte_str)
        posix.close(r)

        status = p.Wait(self.waiter)

        # OSH has the concept of aborting in the middle of a WORD.  We're not
        # waiting until the command is over!
        if self.exec_opts.more_errexit():
            if self.exec_opts.errexit() and status != 0:
                raise error.ErrExit('Command sub exited with status %d (%r)',
                                    status, NewStr(command_str(node.tag_())))
        else:
            # Set a flag so we check errexit at the same time as bash.  Example:
            #
            # a=$(false)
            # echo foo  # no matter what comes here, the flag is reset
            #
            # Set ONLY until this command node has finished executing.

            # HACK: move this
            self.cmd_ev.check_command_sub_status = True
            self.mem.SetLastStatus(status)

        # Runtime errors test case: # $("echo foo > $@")
        # Why rstrip()?
        # https://unix.stackexchange.com/questions/17747/why-does-shell-command-substitution-gobble-up-a-trailing-newline-char
        return ''.join(chunks).rstrip('\n')
Esempio n. 2
0
    def Run(self, cmd_val):
        # type: (cmd_value__Argv) -> int

        # TODO: Also hard usage error here too?
        attrs, arg_r = flag_spec.ParseOilCmdVal('run', cmd_val)
        arg = arg_types.run(attrs.attrs)

        if arg_r.Peek() is None:
            # HARD ERROR, not e_usage(), because errexit is often disabled!
            e_die("'run' expected a command to run", status=2)

        argv, spids = arg_r.Rest2()
        cmd_val2 = cmd_value.Argv(argv, spids, cmd_val.block)

        # Set in the 'except' block, e.g. if 'myfunc' failed
        failure_spid = runtime.NO_SPID
        try:
            # Temporarily turn ON errexit, and blame the 'run' spid.  Note that
            # 'if run myproc' disables it and then enables it!
            with state.ctx_ErrExit(self.mutable_opts, True,
                                   cmd_val.arg_spids[0]):
                # Pass do_fork=True.  Slight annoyance: the real value is a field of
                # command.Simple().  See _NoForkLast() in CommandEvaluator We have an
                # extra fork (miss out on an optimization) of code like ( status ls )
                # or forkwait { status ls }, but that is NOT idiomatic code.  status is
                # for functions.
                status = self.shell_ex.RunSimpleCommand(cmd_val2, True)
                #log('st %d', status)
        except error.ErrExit as e:  # from functino call
            #log('e %d', e.exit_status)
            status = e.exit_status
            failure_spid = e.span_id

        # Do this before -allow-status-01
        if arg.status_ok is not None:
            status = _AdjustStatus(arg.status_ok, status)

        if arg.allow_status_01 and status not in (0, 1):
            if failure_spid != runtime.NO_SPID:
                self.errfmt.Print_('(original failure)', span_id=failure_spid)
                self.errfmt.StderrLine('')

            raise error.ErrExit('fatal: status %d when --allow-status-01' %
                                status,
                                span_id=spids[0],
                                status=status)

        if arg.assign_status is not None:
            var_name = arg.assign_status
            if var_name.startswith(':'):
                var_name = var_name[1:]

            state.SetRefString(self.mem, var_name, str(status))
            return 0  # don't fail

        return status
Esempio n. 3
0
    def RunCommandSub(self, cs_part):
        # type: (command_sub) -> str

        if not self.exec_opts.allow_command_sub():
            # TODO:
            # - Add spid of $(
            # - Better hints.  Use 'run' for 'if myfunc', and 2 lines like local x;
            #   x=$(false) fo assignment builtins.
            # - Maybe we should have an error message ID that links somewhere?

            e_die(
                "Command subs not allowed here because status wouldn't be checked (strict_errexit)."
            )

        node = cs_part.child

        # Hack for weird $(<file) construct
        if node.tag_() == command_e.Simple:
            simple = cast(command__Simple, node)
            # Detect '< file'
            if (len(simple.words) == 0 and len(simple.redirects) == 1
                    and simple.redirects[0].op.id == Id.Redir_Less):
                # change it to __cat < file
                # note: cmd_eval.py _Dispatch works around lack of spid
                tok = Token(Id.Lit_Chars, runtime.NO_SPID, '__cat')
                cat_word = compound_word([tok])
                # MUTATE the command.Simple node.  This will only be done the first
                # time in the parent process.
                simple.words.append(cat_word)

        p = self._MakeProcess(node,
                              inherit_errexit=self.exec_opts.inherit_errexit())

        r, w = posix.pipe()
        p.AddStateChange(process.StdoutToPipe(r, w))
        _ = p.Start()
        #log('Command sub started %d', pid)

        chunks = []  # type: List[str]
        posix.close(w)  # not going to write
        while True:
            byte_str = posix.read(r, 4096)
            if len(byte_str) == 0:
                break
            chunks.append(byte_str)
        posix.close(r)

        status = p.Wait(self.waiter)

        # OSH has the concept of aborting in the middle of a WORD.  We're not
        # waiting until the command is over!
        if self.exec_opts.command_sub_errexit():
            if status != 0:
                raise error.ErrExit('Command sub exited with status %d (%s)' %
                                    (status, ui.CommandType(node)),
                                    span_id=cs_part.left_token.span_id,
                                    status=status)

        else:
            # Set a flag so we check errexit at the same time as bash.  Example:
            #
            # a=$(false)
            # echo foo  # no matter what comes here, the flag is reset
            #
            # Set ONLY until this command node has finished executing.

            # HACK: move this
            self.cmd_ev.check_command_sub_status = True
            self.mem.SetLastStatus(status)

        # Runtime errors test case: # $("echo foo > $@")
        # Why rstrip()?
        # https://unix.stackexchange.com/questions/17747/why-does-shell-command-substitution-gobble-up-a-trailing-newline-char
        return ''.join(chunks).rstrip('\n')