1 | #!/usr/bin/env python3
|
2 | """
|
3 | Testing child process APIs on Windows
|
4 | """
|
5 |
|
6 | import asyncio
|
7 | import sys
|
8 | import os
|
9 | from typing import List
|
10 |
|
11 |
|
12 | async def run_pipeline_async(commands: List[List[str]]) -> List[int]:
|
13 | """Run a pipeline of commands, letting stdout flow naturally, and return exit codes."""
|
14 | if not commands:
|
15 | return []
|
16 |
|
17 | exit_codes = []
|
18 | stdin = None # Initial stdin is None (inherited)
|
19 |
|
20 | # Create pipes for connecting processes
|
21 | processes = []
|
22 |
|
23 | # Set up all processes in the pipeline
|
24 | for i, cmd in enumerate(commands):
|
25 | # For all processes except the last one, create a pipe for stdout
|
26 | if i < len(commands) - 1:
|
27 | # Create a pipe for this process's stdout to the next process's stdin
|
28 | read_fd, write_fd = os.pipe()
|
29 | stdout = write_fd
|
30 | next_stdin = read_fd
|
31 | else:
|
32 | # Last process inherits stdout (terminal or parent process stdout)
|
33 | stdout = None
|
34 | next_stdin = None
|
35 |
|
36 | # Create the process
|
37 | proc = await asyncio.create_subprocess_exec(
|
38 | *cmd,
|
39 | stdin=stdin,
|
40 | stdout=stdout,
|
41 | stderr=None # Inherit stderr
|
42 | )
|
43 |
|
44 | processes.append(proc)
|
45 |
|
46 | # Close write end of pipe in parent process after spawning child
|
47 | if i < len(commands) - 1:
|
48 | os.close(write_fd)
|
49 |
|
50 | # Set up for next iteration
|
51 | stdin = next_stdin
|
52 |
|
53 | # Wait for all processes to complete and collect exit codes
|
54 | for proc in processes:
|
55 | await proc.wait()
|
56 | exit_codes.append(proc.returncode)
|
57 |
|
58 | return exit_codes
|
59 |
|
60 |
|
61 | def RunPipeline(*commands) -> List[int]:
|
62 | """Run a pipeline of commands with natural output flow and return exit codes."""
|
63 | return asyncio.run(run_pipeline_async(commands))
|
64 |
|
65 |
|
66 | def SubprocessDemo():
|
67 | if sys.platform == 'win32':
|
68 | a_list = [['dir', 'build'], ['find', 'sh'], ['find', 'o']]
|
69 |
|
70 | a_list2 = []
|
71 | for a in a_list:
|
72 | a_list2.append(['cmd.exe', '/c'] + a)
|
73 | print(a_list2)
|
74 |
|
75 | #codes = RunPipeline(a_list2[0], a_list2[1], a_list2[2])
|
76 | exit_codes = RunPipeline(*a_list2)
|
77 | print(f"Exit codes: {exit_codes}")
|
78 | return
|
79 |
|
80 | # ls build | grep sh | wc -l
|
81 | exit_codes = RunPipeline(['ls', 'build'], ['grep', 'sh'], ['grep', 'o'])
|
82 | print(f"Exit codes: {exit_codes}")
|
83 |
|
84 | exit_codes = RunPipeline(['ls', 'build'], ['sh', '-c', 'grep py; exit 42'],
|
85 | ['wc', '-l'])
|
86 | print(f"Exit codes: {exit_codes}")
|
87 |
|
88 |
|
89 | # Example usage
|
90 | if __name__ == "__main__":
|
91 | SubprocessDemo()
|