如何通过python程序脚本运行终端命令行命令?

常见方法:

  • 1、subprocess模块

    • subprocess.Popen() 方法【异步非阻塞

      • 典型示例
        import subprocess
        
        def run_with_timeout(command, timeout=5):
            """
            执行命令并监控超时,返回是否成功退出(True=成功,False=超时或失败)
            
            参数:
                command (list): 要执行的命令(如 ["ffmpeg", "-i", "input.mp3"])
                timeout (int): 允许命令运行的最大秒数(默认5秒)
            
            返回:
                bool: True=进程正常退出且返回码为0,False=超时或进程异常退出
            
            逻辑流程:
                1. 启动子进程
                2. 等待进程结束(带超时)
                3. 若超时,先尝试SIGTERM优雅终止
                4. 若仍不退出,用SIGKILL强制终止
                5. 确保资源回收,避免僵尸进程
            """
            # 启动子进程(不捕获输出以节省资源)
            process = subprocess.Popen(
                command,
                stdout=subprocess.DEVNULL,  # 丢弃stdout
                stderr=subprocess.DEVNULL,   # 丢弃stderr
            )
            
            try:
                # 阻塞等待进程结束(主超时)
                exit_code = process.wait(timeout=timeout)
                # 返回是否成功退出(0表示成功)
                return exit_code == 0
                
            except subprocess.TimeoutExpired:
                # --- 第一阶段终止:尝试优雅终止(SIGTERM)---
                process.terminate()
                try:
                    # 给进程2秒时间处理SIGTERM
                    exit_code = process.wait(timeout=2)
                    return False  # 进程已终止,但属于超时后终止,返回失败
                    
                except subprocess.TimeoutExpired:
                    # --- 第二阶段终止:强制杀死(SIGKILL)---
                    process.kill()
                    # 此处wait()会立即返回,因为SIGKILL是同步的
                    process.wait()
                    return False
                    
            # 注:不需要finally关闭流,因为未使用stdout/stderr管道
      • wait(timeout)阻塞等待示例--超时会抛异常
        import subprocess
        
        cmd = 'python test.py'
        process = subprocess.Popen(cmd)  # 开始异步执行
        
        ### 主进程处理别的事情
        
        try:
            # 尝试优雅关闭
            process.terminate()
            exit_code = process.wait(timeout=3)
            print(f"子进程已退出,返回码: {exit_code}")
        except subprocess.TimeoutExpired:
            print("关闭超时,强制杀死进程...")
            process.kill()  # 强制杀死
            exit_code = process.wait()
            print(f"警告:子进程被强制终止,返回码: {exit_code}")
        except Exception as e:
            print(f"关闭进程时出错: {e}")
      • poll()非阻塞等待示例
        import subprocess
        import time
        
        cmd = 'python test.py'
        process = subprocess.Popen(cmd)  # 开始异步执行
        
        ### 主进程处理别的事情
        print("主进程正在处理其他任务...")
        
        # 尝试优雅关闭
        process.terminate()
        
        max_wait_time = 3  # 最大等待时间
        check_interval = 0.1  # 检查间隔
        start_time = time.time()
        
        while True:
            # 非阻塞检查进程状态
            exit_code = process.poll()
            if exit_code is not None:
                # 进程已经结束
                if exit_code == 0:
                    print(f"子进程正常退出,返回码: {exit_code}")
                else:
                    print(f"子进程异常退出,返回码: {exit_code}")
                break
        
            # 检查是否超时
            if time.time() - start_time > max_wait_time:
                print("关闭超时,强制杀死进程...")
                process.kill()  # 强制杀死
        
                # 等待确认进程结束
                final_exit_code = process.wait()
                print(f"子进程被强制终止,返回码: {final_exit_code}")
                break
        
            # 等待一小段时间再次检查
            time.sleep(check_interval)
        
        print("主进程继续执行...")

        poll()会立即返回,进程运行时返回None,结束时返回退出码

      • 不需要交互形式(比如frida执行时按q退出,这个q就是交互参数)
        import subprocess
        import time
        
        # ==================== 启动子进程 ====================
        # 注意:这里故意不启用任何管道(stdin/stdout/stderr)
        process = subprocess.Popen(["python", "test2.py"])
        
        # ==================== 主程序逻辑 ====================
        time.sleep(2)  # 模拟主程序执行其他任务
        
        # ==================== 终止子进程 ====================
        # 1. 温和终止尝试
        process.terminate()  # 发送SIGTERM
        print("已发送终止信号(SIGTERM),等待子进程退出...")
        
        # 2. 超时控制
        try:
            exit_code = process.wait(timeout=5)  # 最多等5秒
            print(f"子进程已退出,返回码: {exit_code}")
        except subprocess.TimeoutExpired:
            # 3. 强制终止
            process.kill()  # 发送SIGKILL
            exit_code = process.wait()  # 立即返回
            print(f"警告:子进程被强制终止,返回码: {exit_code}")
        
        # ==================== 状态验证 ====================
        if exit_code == 0:
            print("子进程正常结束")
        else:
            print(f"子进程异常结束(代码 {exit_code})")
        • 常见信号说明
          • SIGINT       交互式中断    Ctrl+C          os.kill(pid, signal.SIGINT)
          • SIGTERM   优雅终止       kill <PID>     process.terminate()
          • SIGKILL      强制终止        kill -9           process.kill()
      • 交互式退出
        import subprocess
        
        # 启动FFmpeg录制(立即进入等待指令状态)
        process = subprocess.Popen(
            ["ffmpeg", "-f", "pulse", "-i", "default", "output.mp3"],
            # windows则用["ffmpeg", "-f", "dshow", "-i", "audio=麦克风名称","output.mp3"]
            stdin=subprocess.PIPE,  # 必须启用输入管道
            universal_newlines=True
        )
        
        # 通过communicate发送终止指令
        try:
            # 发送'q'指令及响应总时间,最多等待5秒
            _, _ = process.communicate(input="q\n", timeout=5)
        except subprocess.TimeoutExpired:
            # 双重保险:先尝试SIGTERM
            process.terminate()
            try:
                exit_code = process.wait(timeout=5)
            except subprocess.TimeoutExpired:
                process.kill()  # 终极手段
                exit_code = process.wait()  # 立即返回
            if exit_code == 0:
                print("子进程正常结束")
            else:
                print(f"子进程异常结束(代码 {exit_code})")
      • 示例1

        import subprocess, os
        import time
        
        # 启动子进程(异步非阻塞)
        # process = subprocess.Popen("python test2.py")
        process = subprocess.Popen(["python", "test2.py"])
        
        time.sleep(2) # 模拟执行其它任务
        
        # 以下两行命令能保证子进程被完全终止
        # 终止子进程
        process.terminate()  # 或者使用 process.kill()
        # 等待子进程终止
        process.wait() # 没有获取交互结果的能力
        # stdinput, stderr = process.communicate() 用于与子进程进行交互,并等待子进程完成,同时能够获取子进程的标准输出和标准错误输出
        # print(stdinput)
        # print(stderr)
        
        # 判断子进程终止状态
        if process.returncode is not None:
            print("子进程已终止,返回码: ", process.returncode)
        else:
            print("无法确定子进程的终止状态")
      • 示例2

        import subprocess
        
        command = 'adb -s 127.0.0.1:62001 shell "su ; cd /data/local/tmp ; ls"'
        process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        output, error = process.communicate()
        
        if process.returncode == 0:
            print("Command executed successfully.")
            print("Output:\n", output.decode())
        else:
            print("Command execution failed.")
            print("Error:\n", error.decode())
    • subprocess.run() 方法 【同步阻塞】

      • 返回CompletedProcess对象,其中包含有关命令执行的信息,如返回码、标准输出和标准错误输出。
        import subprocess
        
        # 执行外部命令
        result = subprocess.run(["python", "test2.py"], capture_output=True, text=True)
        
        # 获取返回码
        print(f"返回码:{result.returncode}")
        
        # 获取标准输出
        print(f"标准输出:{result.stdout}")
        
        # 获取标准错误输出
        print(f"标准错误输出:{result.stderr}")
    • subprocess.getoutput() 方法 【同步阻塞】

      • 返回标准输出字符串
        import subprocess
        
        # 执行外部命令
        output = subprocess.getoutput("python test2.py")
        
        # 打印标准输出
        print(output)
    • subprocess.check_output()方法【同步阻塞】

      • 返回标准输出字节
        import subprocess
        
        cmd = 'java -jar unidbg-0.9.7.jar'
        result = subprocess.check_output(cmd, shell=True, cwd='unidbg_0_9_7_jar') # cwd参数表示进入某个目录执行的命令
        print(result.strip().decode().splitlines()[0])
  • 2、os模块

    • os.system() 方法【同步阻塞】

      import os
      
      os.system('pip3 show crawlab-sdk') # 查看版本
      # os.system('pip3 uninstall crawlab-sdk -y') # 卸载
      os.system('pip3 install crawlab-sdk==0.3.3 --force-reinstall') # 指定版本强制重新安装
posted @ 2022-09-21 12:37  eliwang  阅读(439)  评论(0)    收藏  举报