杰瑞科技汇

Python popen wait如何等待子进程结束?

Of course! The subprocess.Popen class is the modern, powerful, and flexible way to run external commands in Python. The wait() method is a key part of managing the lifecycle of that process.

Let's break it down.

The Short Answer: What is Popen.wait()?

Popen.wait() is a method that makes your Python script pause and wait for the process started by Popen to complete. It blocks the main script's execution until the external command finishes.

Once the process finishes, wait() returns the exit code of that process.


Detailed Breakdown

The Popen Object

When you use subprocess.Popen(), you are not running the command immediately. Instead, you are creating a Popen object that represents the process. This object gives you a handle to the process, allowing you to control it and get information about it.

import subprocess
# Create a Popen object. The command starts running.
# Note: We use a list of arguments for security and clarity.
process = subprocess.Popen(['ls', '-l'])
print("The Popen object has been created.")
print(f"Process ID (PID): {process.pid}")
print(f"Is the process running? {process.poll() is None}") # poll() checks if it's still running

The wait() Method

If you run the code above, you'll see that the ls -l command runs, but your Python script continues immediately without waiting for it to finish. This is called a non-blocking call.

wait() changes this to a blocking call.

import subprocess
import time
print("Starting the process...")
process = subprocess.Popen(['sleep', '5']) # A command that runs for 5 seconds
print("Process started. Now, we will call wait(). This will pause the script.")
start_time = time.time()
exit_code = process.wait() # The script will hang here for 5 seconds
end_time = time.time()
print(f"The process has finished after {end_time - start_time:.2f} seconds.")
print(f"The exit code of the process is: {exit_code}")

Key characteristics of wait():

  • Blocking: The script pauses at the line process.wait() until the external command is done.
  • Returns Exit Code: It returns the integer exit code of the process. By convention, an exit code of 0 means success, and any non-zero value usually means an error.
  • Process State: After wait() returns, the Popen object's state is updated. process.poll() will no longer be None.

Practical Example: Running git status

Let's use a more realistic example. We want to run git status and capture its output.

import subprocess
try:
    # Create the Popen object to run 'git status'
    # text=True decodes stdout/stderr using default encoding
    # capture_output=True captures stdout and stderr
    # check=True will raise an exception if the command returns a non-zero exit code
    process = subprocess.Popen(
        ['git', 'status'],
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE,
        text=True
    )
    print(f"Started process with PID: {process.pid}")
    print("Waiting for the process to complete...")
    # Wait for the process to finish and get the exit code
    exit_code = process.wait()
    print(f"\nProcess finished with exit code: {exit_code}")
    # After wait(), we can access the captured output
    if exit_code == 0:
        print("\n--- STDOUT ---")
        # stdout is a string because we used text=True
        print(process.stdout)
    else:
        print("\n--- STDERR ---")
        print(process.stderr)
except FileNotFoundError:
    print("Error: 'git' command not found. Is git installed and in your PATH?")
except Exception as e:
    print(f"An error occurred: {e}")

wait() vs. communicate() vs. poll()

This is a crucial distinction for writing robust scripts.

Method Behavior When to Use
wait() Blocks until the process finishes. Returns the exit code. Simple cases where you just need to know when the process is done and don't care about output buffering. Warning: If the process produces a lot of output, it can fill the OS buffer and cause a deadlock if you don't read stdout/stderr.
communicate() Blocks until the process finishes. Reads all of stdout and stderr data, then returns it. This is the safest way to handle output. Highly recommended for most cases. It prevents deadlocks by reading the pipes. It's the standard way to get the output and wait for completion.
poll() Non-blocking. Checks if the process has finished. Returns None if it's still running, or the exit code if it has finished. When you want your script to do other work while waiting for the process. You'd use it in a loop.

Example: The communicate() Best Practice

This is the most common and robust pattern.

import subprocess
# Using communicate() is the preferred method
process = subprocess.Popen(
    ['ls', '-l', '/non_existent_dir'],
    stdout=subprocess.PIPE,
    stderr=subprocess.PIPE,
    text=True
)
# communicate() waits for the process to end and reads all output
stdout_data, stderr_data = process.communicate()
print(f"Process finished with exit code: {process.returncode}")
if process.returncode == 0:
    print("\n--- STDOUT ---")
    print(stdout_data)
else:
    print("\n--- STDERR ---")
    print(stderr_data)

Example: Using poll() in a Loop

This is useful for long-running processes where you don't want to block your entire script.

import subprocess
import time
print("Starting a long-running process (ping)...")
process = subprocess.Popen(['ping', '-c', '5', 'example.com'])
while True:
    # Check if the process has finished
    exit_code = process.poll()
    if exit_code is not None:
        # Process has finished
        print(f"\nProcess finished with exit code: {exit_code}")
        break
    else:
        # Process is still running, do other things
        print("Ping is running... doing other work in the main script.")
        time.sleep(1) # Wait a second before checking again
print("Main script continues after the loop.")

Summary

Method Blocking? Returns Best For
Popen.wait() Yes Exit Code Simple cases where you just need to wait. Be cautious with output.
Popen.communicate() Yes (stdout, stderr) tuple The standard, safest way. Use this when you need to wait and get output.
Popen.poll() No None (if running) or Exit Code Checking status in a non-blocking loop.
分享:
扫描分享到社交APP
上一篇
下一篇