Execute raw CLI commands and returns their output.

def cli(self, commands):
        """Execute raw CLI commands and returns their output."""
        cli_output = {}

        def _count(txt, none):  # Second arg for consistency only. noqa
            """
            Return the exact output, as Junos displays
            e.g.:
            > show system processes extensive | match root | count
            Count: 113 lines
            """
            count = len(txt.splitlines())
            return "Count: {count} lines".format(count=count)

        def _trim(txt, length):
            """
            Trim specified number of columns from start of line.
            """
            try:
                newlines = []
                for line in txt.splitlines():
                    newlines.append(line[int(length) :])
                return "\n".join(newlines)
            except ValueError:
                return txt

        def _except(txt, pattern):
            """
            Show only text that does not match a pattern.
            """
            rgx = "^.*({pattern}).*$".format(pattern=pattern)
            unmatched = [
                line for line in txt.splitlines() if not re.search(rgx, line, re.I)
            ]
            return "\n".join(unmatched)

        def _last(txt, length):
            """
            Display end of output only.
            """
            try:
                return "\n".join(txt.splitlines()[(-1) * int(length) :])
            except ValueError:
                return txt

        def _match(txt, pattern):
            """
            Show only text that matches a pattern.
            """
            rgx = "^.*({pattern}).*$".format(pattern=pattern)
            matched = [line for line in txt.splitlines() if re.search(rgx, line, re.I)]
            return "\n".join(matched)

        def _find(txt, pattern):
            """
            Search for first occurrence of pattern.
            """
            rgx = "^.*({pattern})(.*)$".format(pattern=pattern)
            match = re.search(rgx, txt, re.I | re.M | re.DOTALL)
            if match:
                return "{pattern}{rest}".format(pattern=pattern, rest=match.group(2))
            else:
                return "\nPattern not found"

        def _process_pipe(cmd, txt):
            """
            Process CLI output from Juniper device that
            doesn't allow piping the output.
            """
            if txt is None:
                return txt
            _OF_MAP = OrderedDict()
            _OF_MAP["except"] = _except
            _OF_MAP["match"] = _match
            _OF_MAP["last"] = _last
            _OF_MAP["trim"] = _trim
            _OF_MAP["count"] = _count
            _OF_MAP["find"] = _find
            # the operations order matter in this case!
            exploded_cmd = cmd.split("|")
            pipe_oper_args = {}
            for pipe in exploded_cmd[1:]:
                exploded_pipe = pipe.split()
                pipe_oper = exploded_pipe[0]  # always there
                pipe_args = "".join(exploded_pipe[1:2])
                # will not throw error when there's no arg
                pipe_oper_args[pipe_oper] = pipe_args
            for oper in _OF_MAP.keys():
                # to make sure the operation sequence is correct
                if oper not in pipe_oper_args.keys():
                    continue
                txt = _OF_MAP[oper](txt, pipe_oper_args[oper])
            return txt

        if not isinstance(commands, list):
            raise TypeError("Please enter a valid list of commands!")
        _PIPE_BLACKLIST = ["save"]
        # Preprocessing to avoid forbidden commands
        for command in commands:
            exploded_cmd = command.split("|")
            command_safe_parts = []
            for pipe in exploded_cmd[1:]:
                exploded_pipe = pipe.split()
                pipe_oper = exploded_pipe[0]  # always there
                if pipe_oper in _PIPE_BLACKLIST:
                    continue
                pipe_args = "".join(exploded_pipe[1:2])
                safe_pipe = (
                    pipe_oper
                    if not pipe_args
                    else "{fun} {args}".format(fun=pipe_oper, args=pipe_args)
                )
                command_safe_parts.append(safe_pipe)
            safe_command = (
                exploded_cmd[0]
                if not command_safe_parts
                else "{base} | {pipes}".format(
                    base=exploded_cmd[0], pipes=" | ".join(command_safe_parts)
                )
            )
            raw_txt = self.device.cli(safe_command, warning=False)
            cli_output[py23_compat.text_type(command)] = py23_compat.text_type(
                _process_pipe(command, raw_txt)
            )
        return cli_output