杰瑞科技汇

Python如何执行Python命令行?

下面我将从最简单到最强大,详细介绍几种主流的方法,并附上代码示例和适用场景。

Python如何执行Python命令行?-图1
(图片来源网络,侵删)

核心概念

在开始之前,需要理解一个关键点:你是在执行一个 Python 模块(脚本),还是在执行一个普通的系统命令?

  • 执行 Python 模块:运行一个 .py 文件。python my_script.py
  • 执行系统命令:运行任何在终端/命令行中可以执行的命令,如 ls -l (Linux/macOS) 或 dir (Windows)。

Python 的 subprocess 模块是处理这些任务最现代、最灵活、最强大的工具,下面我们主要以它为例。


subprocess.run() (推荐,Python 3.5+)

这是目前最推荐、最通用的方法,它启动一个子进程来执行命令,并等待其完成。

执行一个简单的 Python 脚本

假设你有一个名为 hello.py 的文件:

Python如何执行Python命令行?-图2
(图片来源网络,侵删)

hello.py

import sys
import time
print(f"Hello from script! My PID is {os.getpid()}")
print(f"Arguments received: {sys.argv}")
time.sleep(5) # 模拟一个耗时操作
print("Script finished.")

你想从另一个 Python 脚本(main.py)中执行它。

main.py

import subprocess
import os
# --- 执行一个 Python 脚本 ---
# 使用 python3 命令来执行 hello.py
# 注意:hello.py 需要参数,可以直接在列表后面添加
command = ["python3", "hello.py", "arg1", "arg2"]
print(f"Executing command: {' '.join(command)}")
# 使用 subprocess.run()
# check=True: 如果命令返回非零退出码(表示失败),则会引发 CalledProcessError 异常
# capture_output=True: 捕获标准输出和标准错误
# text=True: 将输出解码为文本(默认为字节)
# timeout=10: 设置超时时间(秒),超时会引发 TimeoutExpired 异常
try:
    result = subprocess.run(
        command,
        check=True,
        capture_output=True,
        text=True,
        timeout=10
    )
    # 命令执行成功
    print("\n--- Command executed successfully! ---")
    print(f"Return code: {result.returncode}")
    print("Standard Output:")
    print(result.stdout)
except subprocess.CalledProcessError as e:
    # 命令执行失败(返回非零码)
    print(f"\n--- Command failed with return code {e.returncode} ---")
    print("Standard Error:")
    print(e.stderr)
except subprocess.TimeoutExpired as e:
    # 命令执行超时
    print(f"\n--- Command timed out after 10 seconds! ---")
    print("Stdout (if any):")
    print(e.stdout)
    print("Stderr (if any):")
    print(e.stderr)
except FileNotFoundError:
    # python3 命令未找到
    print("Error: 'python3' command not found. Is it installed and in your PATH?")

如何运行:

Python如何执行Python命令行?-图3
(图片来源网络,侵删)
# 确保 hello.py 和 main.py 在同一目录下
python3 main.py

执行系统命令(如 lsdir

执行系统命令的方式完全相同,只是命令列表的第一个元素不是 python3

示例:在 Linux/macOS 上执行 ls -l

import subprocess
command = ["ls", "-l"]
try:
    result = subprocess.run(command, check=True, capture_output=True, text=True)
    print("Command executed successfully!")
    print("Output:")
    print(result.stdout)
except subprocess.CalledProcessError as e:
    print(f"Command failed: {e.stderr}")

os.system() (简单但过时)

这是一个非常老派的方法,直接调用系统的 shell 来执行命令,它简单易用,但功能有限,不推荐在新代码中使用。

特点:

  • 直接在父进程中执行,无法获取命令的输出(除非重定向到文件)。
  • 无法处理超时。
  • 容易受到 shell 注入攻击(如果命令参数来自用户输入)。
import os
# 执行一个简单的命令
# 返回的是命令执行后的退出码
return_code = os.system("echo 'Hello from os.system'")
print(f"Return code: {return_code}")
# 执行一个 Python 脚本
# 注意:如果脚本路径或参数包含空格或特殊字符,可能会有问题
os.system("python3 hello.py arg1 arg2")

os.popen() (旧版管道)

os.popen 打开一个管道,允许你通过文件对象来读取命令的输出或写入输入。

特点:

  • 可以获取命令的输出。
  • 功能比 os.system 强,但比 subprocess 弱。
  • Python 3.3+ 中已废弃,不推荐使用。
import os
# 执行命令并读取输出
# 'r' 表示以读取模式打开管道
with os.popen('ls -l') as pipe:
    output = pipe.read()
    print("Output from os.popen:")
    print(output)

subprocess.Popen() (最灵活)

subprocess.run() 实际上是 subprocess.Popen() 的一个高级封装,当你需要更精细的控制时,比如与子进程进行实时交互、并发运行多个进程等,就应该直接使用 Popen

Popen 是异步的,它会立即返回一个 Popen 对象,而不会等待子进程结束,你需要手动调用 communicate()wait()poll() 来管理进程。

import subprocess
import time
print("Starting a long-running process with Popen...")
# 启动进程
# Popen 不会阻塞,它会立即返回
process = subprocess.Popen(
    ["python3", "hello.py", "from_popen"],
    stdout=subprocess.PIPE,
    stderr=subprocess.PIPE,
    text=True
)
# 主线程可以做其他事情
print("Main program is doing other work...")
time.sleep(2)
# 等待进程结束并获取输出
# communicate() 会等待进程结束,并返回 (stdout, stderr) 的元组
# 它还会正确处理子进程的管道,避免死锁
stdout, stderr = process.communicate()
print("\n--- Popen process finished ---")
print(f"Return code: {process.returncode}")
print("Standard Output:")
print(stdout)
if stderr:
    print("Standard Error:")
    print(stderr)

import (Pythonic 的方式)

如果你的目标只是调用另一个 Python 脚本中的函数或使用其类,而不是完全作为独立进程运行,那么最好的方法是直接导入它。

my_module.py

def greet(name):
    return f"Hello, {name} from my_module!"
def add(a, b):
    return a + b

main.py

import my_module
# 直接调用函数,就像调用本地函数一样
message = my_module.greet("Alice")
print(message)
result = my_module.add(10, 20)
print(f"10 + 20 = {result}")

优点:

  • 高效:没有创建新进程的开销。
  • 简单:代码更清晰,符合 Python 的哲学。
  • 共享内存:可以直接共享变量和数据结构。

缺点:

  • 共享全局状态:两个脚本运行在同一个解释器中,会共享全局命名空间,可能导致意外的副作用。
  • 无法隔离:一个脚本的错误会影响整个程序。

总结与选择指南

方法 适用场景 优点 缺点
subprocess.run() 绝大多数情况下的首选,执行命令、运行脚本、获取输出、处理错误。 现代、安全、功能强大、接口清晰、支持超时。 相对复杂一点,但非常值得学习。
subprocess.Popen() 需要高级控制,如并发实时交互、管理多个子进程。 最灵活、功能最全、异步。 代码更复杂,需要手动管理进程生命周期。
import 想在当前程序中复用另一个 Python 文件的代码(函数、类) 最高效、最简单、Pythonic。 共享全局状态,无法隔离,不适用于需要独立环境的场景。
os.system() 简单的一次性命令,且不关心输出和错误。 极其简单。 功能弱、不安全(易受注入)、无法获取输出、已过时。
os.popen() 需要获取命令输出的旧代码中可能见到。 os.system 强。 已废弃,功能不如 subprocess

简单决策流程:

  1. 我只是想用另一个 .py 文件里的函数?

    • import
  2. 我需要运行一个独立的 .py 脚本,或者一个系统命令(如 git, docker),并获取它的输出和结果?

    • subprocess.run() 这是你的默认选择。
  3. 我需要同时运行多个命令,或者与一个长时间运行的进程进行实时对话吗?

    • subprocess.Popen()
分享:
扫描分享到社交APP
上一篇
下一篇