def run_parallel_processes(command_list: list, max_processes=-1):
    """
    Runs commands in a list in parallel and waits for them to complete

    :param max_processes:
    :param command_list:
    :return: list of commands that failed
    """
    import subprocess
    from tqdm import tqdm
    from functools import partial
    from multiprocessing import cpu_count
    from multiprocessing.dummy import Pool
    n = len(command_list)
    processor_pool_size = cpu_count() if n > cpu_count() else n
    processor_pool_size = (max_processes if max_processes > 0 else
        processor_pool_size)
    failed_commands = []
    pool = Pool(processor_pool_size)
    for i, response_code in tqdm(enumerate(pool.imap(partial(subprocess.
        call, shell=True), command_list)), total=n):
        if response_code != 0:
            failed_commands.append(command_list[i])
    return failed_commands
