Execute a command with subprocess.Popen and block until output

    Args:
        cmd (tuple or str): same as subprocess.Popen args

    Keyword Arguments:
        ignore_error (bool): if False, raise an Exception if p.returncode is
            not 0
        cwd (str): current working directory path to run cmd with
        shell (bool): subprocess.Popen ``shell`` kwarg

    Returns:
        str: stdout output of wrapped call to ``sh`` (``subprocess.Popen``)

    Raises:
        Exception: if ignore_error is true and returncode is not zero

    .. note:: this executes commands with ``shell=True``: careful with
       shell-escaping.

def sh(cmd, ignore_error=False, cwd=None, shell=False, **kwargs):
    """
    Execute a command with subprocess.Popen and block until output

    Args:
        cmd (tuple or str): same as subprocess.Popen args

    Keyword Arguments:
        ignore_error (bool): if False, raise an Exception if p.returncode is
            not 0
        cwd (str): current working directory path to run cmd with
        shell (bool): subprocess.Popen ``shell`` kwarg

    Returns:
        str: stdout output of wrapped call to ``sh`` (``subprocess.Popen``)

    Raises:
        Exception: if ignore_error is true and returncode is not zero

    .. note:: this executes commands with ``shell=True``: careful with
       shell-escaping.

    """
    kwargs.update({
        'shell': shell,
        'cwd': cwd,
        'stderr': subprocess.STDOUT,
        'stdout': subprocess.PIPE,})
    log.debug((('cmd', cmd), ('kwargs', kwargs)))
    p = subprocess.Popen(cmd, universal_newlines=True, **kwargs)
    p_stdout = p.communicate()[0]
    if p.returncode and not ignore_error:
        raise subprocess.CalledProcessError(p.returncode, cmd, p_stdout)
    return p_stdout