Python
os.system
subprocess
capture output
screen output suppression

Assign output of os.system to a variable and prevent it from being displayed on the screen

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

os.system is the wrong tool if you want command output in a Python variable. It only returns the command's exit status, while the command's standard output and standard error still go to the terminal unless you redirect them manually.

Why os.system Does Not Capture Output

This code returns an integer status, not the printed text:

python
1import os
2
3status = os.system("python3 -c \"print('hello')\"")
4print("status:", status)

You will see hello on the screen because the child process writes directly to the terminal. The variable status only contains the process exit code.

So if your real requirement is:

  • capture command output
  • suppress display on the screen
  • inspect return code cleanly

then subprocess is the modern answer.

Use subprocess.run to Capture Output

The most direct replacement is subprocess.run with capture_output=True and text=True:

python
1import subprocess
2
3result = subprocess.run(
4    ["python3", "-c", "print('hello')"],
5    capture_output=True,
6    text=True,
7    check=False,
8)
9
10output = result.stdout
11print("captured:", output.strip())
12print("exit code:", result.returncode)

Nothing is printed unless you print it yourself. That is already a better fit than os.system.

Suppress Output Completely

If you do not need the text at all and only care whether the command succeeded, send both streams to DEVNULL:

python
1import subprocess
2
3completed = subprocess.run(
4    ["python3", "-c", "print('hidden'); import sys; print('warn', file=sys.stderr)"],
5    stdout=subprocess.DEVNULL,
6    stderr=subprocess.DEVNULL,
7)
8
9print(completed.returncode)

This is cleaner than putting shell redirection syntax into a command string.

Capture Standard Output and Standard Error Separately

Real programs often need to keep output for later inspection:

python
1import subprocess
2
3result = subprocess.run(
4    ["python3", "-c", "import sys; print('ok'); print('problem', file=sys.stderr)"],
5    capture_output=True,
6    text=True,
7)
8
9print("stdout:", result.stdout.strip())
10print("stderr:", result.stderr.strip())

Keeping the two streams separate is usually better than blindly mixing them.

Prefer Argument Lists to Shell Strings

When you do not need shell features such as pipes or globbing, pass the command as a list:

python
1import subprocess
2
3name = "world"
4result = subprocess.run(
5    ["echo", f"hello {name}"],
6    capture_output=True,
7    text=True,
8    check=True,
9)
10
11print(result.stdout.strip())

This avoids many quoting bugs and reduces shell-injection risk. Use shell=True only when you actually need shell syntax and understand the security tradeoff.

Timeouts and Error Handling

One reason subprocess is better is that it handles operational problems more explicitly:

python
1import subprocess
2
3try:
4    result = subprocess.run(
5        ["python3", "-c", "import time; time.sleep(2); print('done')"],
6        capture_output=True,
7        text=True,
8        timeout=1,
9        check=True,
10    )
11except subprocess.TimeoutExpired:
12    print("command timed out")
13except subprocess.CalledProcessError as exc:
14    print("command failed:", exc.returncode)
15    print(exc.stderr)

That is much easier to reason about than a shell string handed to os.system.

When check_output Is Enough

If you only want standard output and want Python to raise automatically on non-zero exit status, subprocess.check_output is a compact option:

python
1import subprocess
2
3output = subprocess.check_output(
4    ["python3", "-c", "print(42)"],
5    text=True,
6)
7
8print(output.strip())

It is less flexible than subprocess.run, but useful for small scripts.

Common Pitfalls

  • Expecting os.system to return the command's printed output instead of only the exit status.
  • Using shell redirection strings when subprocess already provides explicit output controls.
  • Suppressing both stdout and stderr too early and losing useful debugging information.
  • Passing a single shell string when a list of arguments would be safer and clearer.
  • Forgetting timeout handling for commands that may hang.

Summary

  • 'os.system returns a status code, not the command output text.'
  • Use subprocess.run to capture output into variables.
  • Use DEVNULL if you want the command to stay quiet on screen.
  • Prefer list-based arguments over shell strings when you do not need shell features.
  • Add timeout and error handling if the command is part of real application logic.

Course illustration
Course illustration

All Rights Reserved.