Skip to content

Command Helpers

These are helpers related to executing shell commands. They are used throughout BBOT and its modules for executing various binaries such as nmap, nuclei, etc.

Note that these helpers can be invoked directly from self.helpers, e.g.:

self.helpers.run("ls", "-l")

run async

run(self, *command, check=False, text=True, **kwargs)

Runs a command asynchronously and gets its output as a string.

This method is a simple helper for executing a command and capturing its output.
If an error occurs during execution, it can optionally raise an error or just log the stderr.

Args:
    *command (str): The command to run as separate arguments.
    check (bool, optional): If set to True, raises an error if the subprocess exits with a non-zero status.
                            Defaults to False.
    text (bool, optional): If set to True, decodes the subprocess output to string. Defaults to True.
    **kwargs (dict): Additional keyword arguments for the subprocess.

Returns:
    CompletedProcess: A completed process object with attributes for the command, return code, stdout, and stderr.

Raises:
    CalledProcessError: If the subprocess exits with a non-zero status and `check=True`.

Examples:
    >>> process = await run(["ls", "/tmp"])
    >>> process.stdout
    "file1.txt

file2.txt"

Source code in bbot/core/helpers/command.py
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
async def run(self, *command, check=False, text=True, **kwargs):
    """Runs a command asynchronously and gets its output as a string.

    This method is a simple helper for executing a command and capturing its output.
    If an error occurs during execution, it can optionally raise an error or just log the stderr.

    Args:
        *command (str): The command to run as separate arguments.
        check (bool, optional): If set to True, raises an error if the subprocess exits with a non-zero status.
                                Defaults to False.
        text (bool, optional): If set to True, decodes the subprocess output to string. Defaults to True.
        **kwargs (dict): Additional keyword arguments for the subprocess.

    Returns:
        CompletedProcess: A completed process object with attributes for the command, return code, stdout, and stderr.

    Raises:
        CalledProcessError: If the subprocess exits with a non-zero status and `check=True`.

    Examples:
        >>> process = await run(["ls", "/tmp"])
        >>> process.stdout
        "file1.txt\nfile2.txt"
    """
    proc, _input, command = await self._spawn_proc(*command, **kwargs)
    if proc is not None:
        if _input is not None:
            if isinstance(_input, (list, tuple)):
                _input = b"\n".join(smart_encode(i) for i in _input) + b"\n"
            else:
                _input = smart_encode(_input)
        stdout, stderr = await proc.communicate(_input)

        # surface stderr
        if text:
            if stderr is not None:
                stderr = smart_decode(stderr)
            if stdout is not None:
                stdout = smart_decode(stdout)
        if proc.returncode:
            if check:
                raise CalledProcessError(proc.returncode, command, output=stdout, stderr=stderr)
            if stderr:
                command_str = " ".join(command)
                log.warning(f"Stderr for run({command_str}):\n\t{stderr}")

        return CompletedProcess(command, proc.returncode, stdout, stderr)

run_live async

run_live(self, *command, check=False, text=True, **kwargs)

Runs a command asynchronously and iterates through its output line by line in realtime.

This method is useful for executing a command and capturing its output on-the-fly, as it is generated. If an error occurs during execution, it can optionally raise an error or just log the stderr.

Parameters:

  • *command (str, default: () ) –

    The command to run as separate arguments.

  • check (bool, default: False ) –

    If set to True, raises an error if the subprocess exits with a non-zero status. Defaults to False.

  • text (bool, default: True ) –

    If set to True, decodes the subprocess output to string. Defaults to True.

  • **kwargs (dict, default: {} ) –

    Additional keyword arguments for the subprocess.

Yields:

  • str or bytes: The output lines of the command, either as a decoded string (if text=True) or as bytes (if text=False).

Raises:

Examples:

>>> async for line in run_live(["tail", "-f", "/var/log/auth.log"]):
...     log.info(line)
Source code in bbot/core/helpers/command.py
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
async def run_live(self, *command, check=False, text=True, **kwargs):
    """Runs a command asynchronously and iterates through its output line by line in realtime.

    This method is useful for executing a command and capturing its output on-the-fly, as it is generated.
    If an error occurs during execution, it can optionally raise an error or just log the stderr.

    Args:
        *command (str): The command to run as separate arguments.
        check (bool, optional): If set to True, raises an error if the subprocess exits with a non-zero status.
                                Defaults to False.
        text (bool, optional): If set to True, decodes the subprocess output to string. Defaults to True.
        **kwargs (dict): Additional keyword arguments for the subprocess.

    Yields:
        str or bytes: The output lines of the command, either as a decoded string (if `text=True`)
                      or as bytes (if `text=False`).

    Raises:
        CalledProcessError: If the subprocess exits with a non-zero status and `check=True`.

    Examples:
        >>> async for line in run_live(["tail", "-f", "/var/log/auth.log"]):
        ...     log.info(line)
    """
    proc, _input, command = await self._spawn_proc(*command, **kwargs)
    if proc is not None:
        input_task = None
        if _input is not None:
            input_task = asyncio.create_task(_write_stdin(proc, _input))

        while 1:
            try:
                line = await proc.stdout.readline()
            except ValueError as e:
                command_str = " ".join([str(c) for c in command])
                log.warning(f"Error executing command {command_str}: {e}")
                log.trace(traceback.format_exc())
                continue
            if not line:
                break
            if text:
                line = smart_decode(line).rstrip("\r\n")
            else:
                line = line.rstrip(b"\r\n")
            yield line

        if input_task is not None:
            try:
                await input_task
            except ConnectionError:
                log.trace(f"ConnectionError in command: {command}, kwargs={kwargs}")
                log.trace(traceback.format_exc())
        await proc.wait()

        if proc.returncode:
            stdout, stderr = await proc.communicate()
            if text:
                if stderr is not None:
                    stderr = smart_decode(stderr)
                if stdout is not None:
                    stdout = smart_decode(stdout)
            if check:
                raise CalledProcessError(proc.returncode, command, output=stdout, stderr=stderr)
            # surface stderr
            if stderr:
                command_str = " ".join(command)
                log.warning(f"Stderr for run_live({command_str}):\n\t{stderr}")