Python subprocess.run Command: Capture Output Safely
Run a known local Python child process with separate arguments, capture output, and handle status and timeout.
Example check: Worked and guided code samples are checked against CPython 3.13. Practice uses Pyodide 0.29.5 (Python 3.13) in your browser. How we check lessons
Video lesson: Python subprocess.run Command: Capture Output Safely
The preview is stored on this site. YouTube loads only when you press Play. Watch on YouTube
Jump to a chapter
Read the video transcript
Select a timestamp to watch that moment on YouTube.
- 00:00
subprocess.run launches a child process on your local machine and waits for it. Pass a sequence of program and argument strings rather than one shell command string. In this tutorial the executable is sys.executable, which selects the same Python interpreter as the parent. Use capture_output=True and text=True when you want stdout and stderr as decoded strings. The first code sample is for a normal local Python interpreter. sys.executable identifies the interpreter already running the parent, and the list gives it -c and a fixed code string as separate arguments. subprocess.run waits for the child.
- 00:40
capture_output=True stores stdout and stderr; text=True decodes them into strings; timeout=5 bounds the wait. Notice what capture_output does not do: it does not decide whether the child succeeded. A process can write a helpful message to stdout and still exit nonzero, or write a warning to stderr and still exit zero. Read returncode or request check=True based on the caller's policy. Do not infer success solely from nonempty text. The second local run makes this visible by printing a diagnostic and exiting with three. Check the CompletedProcess.returncode, or use check=True to raise CalledProcessError
- 01:23
for a nonzero exit. Set a timeout so a stuck child cannot block forever. The video demonstrates an actual local Python child that prints a greeting, then a failing child whose stderr is captured. A string such as 'python -c ...' with the default shell=False is not automatically split into a program and its arguments. The browser exercise cannot launch an operating-system child. It builds the same argv shape but leaves execution to a local script. Do not interpret a browser failure to run subprocess as a defect in the command builder. Copy the sample into a local Python 3 process to observe the child return code
- 02:06
and greeting. The second local child writes bad to stderr and exits with status 3. With check=False, the default, subprocess.run still returns a CompletedProcess: its returncode is 3 and its stderr contains the message. If you set check=True, the call instead raises CalledProcessError for that nonzero status. Keep user input as a separate argument, not part of the code string or a shell command. The child receives it through sys.argv. shell=True changes the security model and is unnecessary for this known Python command.
- 02:46
Production callers should also decide which working directory and environment the child may inherit, and should avoid printing secrets from captured streams. A useful caller must choose an error policy rather than merely print captured text. Preserve stderr for diagnostics, avoid showing sensitive environment details to end users, and set timeout for potentially stalled children. For a trusted Python child, no shell is needed. A browser-based Python runtime cannot launch operating-system child processes. The interactive task here therefore builds and validates an argv list but never claims to run it.
- 03:26
Copy the local example into a normal Python 3 environment to see subprocess.run in action. This page complements the site's CLI exit- code and shlex lessons: one teaches how programs report status; this one teaches a caller how to launch and inspect a known child. The browser-safe example only constructs argv and prints its length and final item. Punctuation in the name stays in that one data argument. A shell would interpret some punctuation as syntax, so avoid concatenating untrusted text into a command string or enabling shell=True without a concrete reason.
- 04:06
The checked task rejects blank names and returns a four-element list. It never calls subprocess in the browser. In local code, use that list with subprocess.run and consider a fixed cwd and narrowly selected environment if the child should not inherit all of the parent process's context. The lesson is about a known program, not running arbitrary commands supplied by a visitor. Return an argument list [sys.executable, '-c', fixed_code, name] from greeting_command(name), where fixed_code prints Hello, followed by sys.argv[1]. Reject a blank name with ValueError. This browser task only builds argv; run subprocess.run with the list in local
- 04:48
Python as shown in the guide. We use the same Python interpreter as the parent so the demonstration does not depend on a python command being installed on PATH. If you instead launch a project tool, use a known executable path and keep its arguments separate. A timeout limits how long run waits after the child starts, but process startup itself may still take time. Handling TimeoutExpired and choosing whether to retry are operational decisions for a larger application. The example deliberately does not execute arbitrary visitor input. The only dynamic value is the name passed as one argument.
- 05:31
This distinction is essential: shell punctuation inside that name remains data when no shell is invoked. If a workflow truly needs a shell pipeline, review the shell's quoting and trust boundary separately. For this greeting child, shell=True would add complexity and risk without adding value. At PythonLessonLab, work through the matching lesson and run the checked task. Explain the output before changing the code, then make one small change and rerun the boundary case. The result matters more than memorizing a function name: you should know what it accepts, what it returns, and which failure it prevents.
Understand the concept
subprocess.run launches a child process on your local machine and waits for it. Pass a sequence of program and argument strings rather than one shell command string. In this tutorial the executable is sys.executable, which selects the same Python interpreter as the parent. Use capture_output=True and text=True when you want stdout and stderr as decoded strings.
Check the CompletedProcess.returncode, or use check=True to raise CalledProcessError for a nonzero exit. Set a timeout so a stuck child cannot block forever. The video demonstrates an actual local Python child that prints a greeting, then a failing child whose stderr is captured. A string such as 'python -c ...' with the default shell=False is not automatically split into a program and its arguments.
Keep user input as a separate argument, not part of the code string or a shell command. The child receives it through sys.argv. shell=True changes the security model and is unnecessary for this known Python command. Production callers should also decide which working directory and environment the child may inherit, and should avoid printing secrets from captured streams.
A browser-based Python runtime cannot launch operating-system child processes. The interactive task here therefore builds and validates an argv list but never claims to run it. Copy the local example into a normal Python 3 environment to see subprocess.run in action. This page complements the site's CLI exit-code and shlex lessons: one teaches how programs report status; this one teaches a caller how to launch and inspect a known child.
- Pass a list of program and arguments
- capture_output and text return readable streams
- check=True raises on nonzero status
- timeout bounds the wait for a child
See it step by step
Read the code, predict the output, then compare it with the result.
01. Run a known child locally
# Run this in local Python, not the browser console.
import subprocess,sys
r=subprocess.run([sys.executable,'-c','print("Hello, Ada")'],capture_output=True,text=True,timeout=5)
print(r.returncode,r.stdout.strip())0 Hello, AdaThe child inherits the selected Python executable and its stdout is captured.
Run on your computer: this example needs a local Python environment.
02. Inspect a nonzero status
# Run this in local Python, not the browser console.
import subprocess,sys
r=subprocess.run([sys.executable,'-c','import sys; print("bad",file=sys.stderr); sys.exit(3)'],capture_output=True,text=True,timeout=5)
print(r.returncode,r.stderr.strip())3 badWithout check=True, the result carries both the failure status and error text.
Run on your computer: this example needs a local Python environment.
03. Build argv without executing it
import sys
code='import sys; print(sys.argv[1])'
argv=[sys.executable,'-c',code,'Ada; echo unsafe']
print(len(argv),argv[-1])4 Ada; echo unsafeThe final string stays one data argument rather than shell syntax.
A closer look
Follow the reasoning, inspect each result, then try the suggested changes in the console below.
Run a known Python child on a local machine
The first code sample is for a normal local Python interpreter. sys.executable identifies the interpreter already running the parent, and the list gives it -c and a fixed code string as separate arguments. subprocess.run waits for the child. capture_output=True stores stdout and stderr; text=True decodes them into strings; timeout=5 bounds the wait.
The browser exercise cannot launch an operating-system child. It builds the same argv shape but leaves execution to a local script. Do not interpret a browser failure to run subprocess as a defect in the command builder. Copy the sample into a local Python 3 process to observe the child return code and greeting.
# Run this in local Python, not the browser console.
import subprocess,sys
r=subprocess.run([sys.executable,'-c','print("Hello, Ada")'],capture_output=True,text=True,timeout=5)
print(r.returncode,r.stdout.strip())0 Hello, AdaRun on your computer: this example needs a local Python environment.
- Use sys.executable rather than guessing a python executable name.
- Keep executable, switch, code, and data in four argv elements.
- Run locally and inspect returncode and stdout.
Inspect a failure and stderr separately
The second local child writes bad to stderr and exits with status 3. With check=False, the default, subprocess.run still returns a CompletedProcess: its returncode is 3 and its stderr contains the message. If you set check=True, the call instead raises CalledProcessError for that nonzero status.
A useful caller must choose an error policy rather than merely print captured text. Preserve stderr for diagnostics, avoid showing sensitive environment details to end users, and set timeout for potentially stalled children. For a trusted Python child, no shell is needed.
# Run this in local Python, not the browser console.
import subprocess,sys
r=subprocess.run([sys.executable,'-c','import sys; print("bad",file=sys.stderr); sys.exit(3)'],capture_output=True,text=True,timeout=5)
print(r.returncode,r.stderr.strip())3 badRun on your computer: this example needs a local Python environment.
- Run the deliberately failing child locally.
- Compare returncode, stdout, and stderr.
- Decide whether check=True fits the caller's policy.
Keep user text as one argument
The browser-safe example only constructs argv and prints its length and final item. Punctuation in the name stays in that one data argument. A shell would interpret some punctuation as syntax, so avoid concatenating untrusted text into a command string or enabling shell=True without a concrete reason.
The checked task rejects blank names and returns a four-element list. It never calls subprocess in the browser. In local code, use that list with subprocess.run and consider a fixed cwd and narrowly selected environment if the child should not inherit all of the parent process's context. The lesson is about a known program, not running arbitrary commands supplied by a visitor.
import sys
code='import sys; print(sys.argv[1])'
argv=[sys.executable,'-c',code,'Ada; echo unsafe']
print(len(argv),argv[-1])4 Ada; echo unsafe- Validate the name before constructing argv.
- Keep the fixed code separate from the name.
- Use the list only in a trusted local Python process.
Try it in Python
Edit the example and run it. Python starts in your browser the first time you click Run.
Python console
Ready to runNeed input()? Add one value per line
Your output appears here.
Need a hint?
Keep code as a fixed string, validate name.strip(), and return four separate list elements. Do not join the list into a shell command.
Complete the task and select Check task to verify your code.
Quick quiz
Three questions. You can change your answers and try again.
Typical mistakes
Everyone meets these errors. See what causes them and how to fix them.
Passing one unsplit command string
subprocess.run('python -c code Ada')subprocess.run([sys.executable, '-c', code, 'Ada'])What happens: The operating system may look for a program with that entire name.
Use a sequence to identify executable and arguments precisely.
Interpolating data into shell syntax
subprocess.run(f'python child.py {name}', shell=True)subprocess.run([sys.executable, 'child.py', name])What happens: User-controlled text can change what the shell executes.
Keep untrusted text in an argv element and avoid an unnecessary shell.