docs: def run(cls, command, timeout=None, cwd=None, env=None, debug=None):
          """
          Runs a given command on the system within a set time period, providing an easy way to access
          command output as it happens without waiting for the command to finish running.
  
          :type list
          :param command: Should be a list that contains the command that should be ran on the given
                          system. The only whitespaces that can occur is for paths that use a backslash
                          to escape it appropriately
  
          :type int
          :param timeout: Specificed in seconds. If a command outruns the timeout then the command and
                          its child processes will be terminated. The default is to run
  
          :type string
          :param cwd: If cwd is set then the current directory will be changed to cwd before it is executed.
                      Note that this directory is not considered when searching the executable, so you
                      can’t specify the program’s path relative to cwd.
  
          :type dict
          :param env: A dict of any ENV variables that should be combined into the OS ENV that will help
                      the command to run successfully. Note that more often than not the command run
                      does not have the same ENV variables available as your shell by default and as such
                      require some assistance.
  
          :type function
          :param debug: A function (also a class function) can be passed in here and all output, line by line,
                        from the command being run will be passed to it as it gets outputted to stdout.
                        This allows for things such as logging (using the built in python logging lib)
                        what is happening on long running commands or redirect output of a tail -f call
                        as lines get outputted without having to wait till the command finishes.
  
          :return returns :class:`Command.Response` that contains the exit code and the output from the command
          """
  
          # Merge together the system ENV details and the passed in ones if any
          environ = dict(os.environ)
          environ.update(env or {})
  
          # If string is passed in for the command then attempt to split it up
          if isinstance(command, str):
              command = shlex.split(command)
  
          # Check if the executable is executable and in fact exists
          which(command[0], environ)
  
          # Use tempfile to get past a limitation with subprocess.PIPE and 64kb.
          # Also to have a file to plug into the track generator
          outputtmp = tempfile.NamedTemporaryFile()
  
          try:
              # Verify debug is in fact a function we can call
              debug_output = False
              if debug and (callable(debug) or hasattr(debug, "__call__")):
                  debug_output = True
  
              if debug_output:
                  def track(thefile, shutdown=None):
                      """Process the temp file until a valid line is found and return it"""
                      thefile.seek(0, 2)
                      while True:
                          if shutdown and shutdown.isSet():
                              break
  
                          line = thefile.readline()
                          if not line:
                              time.sleep(0.00001)
                              continue
                          yield line
  
                  def track_run(output, debug, shutdown_event):
                      """Wrap track and pass information on the fly to the debug process"""
                      for line in track(output, shutdown_event):
                          debug(line.rstrip())
  
                  # Run the track generator in a separate thread so we can run the command
                  shutdown_event = threading.Event()
                  thread = threading.Thread(
                      target=track_run,
                      args=(open(outputtmp.name, 'rb'), debug, shutdown_event),
                      name='Monitoring'
                  )
                  thread.start()
  
              def target():
                  # Run the actual command
                  cls.process = subprocess.Popen(
                      command,
                      universal_newlines=True,
                      shell=False,
                      env=environ,
                      cwd=cwd,
                      preexec_fn=os.setsid,
                      stdout=outputtmp,
                      stderr=outputtmp
                  )
  
                  cls.process.communicate()
  
              # Deal with timeout
              thread = threading.Thread(target=target, name='Command Runner')
              thread.start()
              thread.join(timeout)
  
              if thread.is_alive():
                  cls.process.terminate()
                  thread.join()
                  if thread.is_alive():
                      cls.process.kill()
  
              # Prime the response
              response = Response
              response.command = command
              response.exit = cls.process.returncode
  
              # Fetch from the temp file
              outputtmp.seek(0, 0)
              response.output = outputtmp.read().strip()
              outputtmp.close()
  
              if response.exit < 0:
                  raise CommandException("command ('%s') was terminated by signal: %s"
                                         % (' '.join(command), -response.exit),
                                         response.exit,
                                         response.output)
              if response.exit > 0:
                  raise CommandException("command ('%s') exited with value: %s\n\n%s"
                                         % (' '.join(command), str(response.exit), response.output),
                                         response.exit,
                                         response.output)
  
              return response
          except OSError as error:
              # Use fake exit code since we can't get the accurate one from this
              raise CommandException("command failed: %s" % error, 1, error)
          except subprocess.CalledProcessError as error:
              raise CommandException(error.output, error.returncode, error.output)
          finally:
              if debug_output:
                  shutdown_event.set()