Python
subprocess
subprocess.call()
programming
debugging

Retrieving the output of subprocess.call

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

subprocess.call() does not return a command's stdout. It only runs the process, waits for it to finish, and returns the exit code. If you need the actual output, use subprocess.run() with output capture, subprocess.check_output(), or Popen for streaming control.

What subprocess.call() Actually Returns

The most common misunderstanding is treating subprocess.call() as if it returns the text printed by the child process.

python
1import subprocess
2
3code = subprocess.call(["python", "-c", "print('hello')"])
4print(code)  # usually 0

The child prints hello to the terminal, but the Python variable receives only the process return code. That is by design.

So if your question is "how do I retrieve the output," the first answer is: call() is the wrong API for that job.

Use subprocess.run() in Modern Python

For current Python code, subprocess.run() is usually the best default. It can capture both stdout and stderr.

python
1import subprocess
2
3result = subprocess.run(
4    ["python", "-c", "print('hello')"],
5    capture_output=True,
6    text=True,
7    check=False,
8)
9
10print(result.returncode)  # 0
11print(result.stdout)      # hello
12print(result.stderr)      # empty string

This is the modern, readable replacement for many older call() and Popen() use cases.

If you want the command to raise on failure:

python
1result = subprocess.run(
2    ["python", "-c", "print('ok')"],
3    capture_output=True,
4    text=True,
5    check=True,
6)

Then a non-zero exit status becomes a CalledProcessError.

Use check_output() for Stdout Only

If you only care about stdout and want a small API surface, subprocess.check_output() is still useful.

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

This is concise, but it is less flexible than run() because the return value is just the captured stdout.

For new code, run() is often the clearer choice.

Capture Both Stdout and Stderr

Sometimes the real diagnostic information is on stderr, not stdout.

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

This separation is helpful for:

  • command wrappers
  • CI tooling
  • debugging build failures
  • scripts that need clean machine-readable stdout

If you deliberately want stderr merged into stdout, redirect it:

python
1result = subprocess.run(
2    ["python", "-c", "import sys; print('out'); print('err', file=sys.stderr)"],
3    stdout=subprocess.PIPE,
4    stderr=subprocess.STDOUT,
5    text=True,
6)
7
8print(result.stdout)

Use Popen for Streaming or Interactive Cases

run() is great when you can wait for the whole command to finish. Use Popen when you need more control.

python
1import subprocess
2
3process = subprocess.Popen(
4    ["python", "-c", "print('line 1'); print('line 2')"],
5    stdout=subprocess.PIPE,
6    stderr=subprocess.PIPE,
7    text=True,
8)
9
10stdout, stderr = process.communicate()
11
12print("return code:", process.returncode)
13print("stdout:", stdout)
14print("stderr:", stderr)

That pattern is useful for long-running commands, interactive tools, or streaming output line by line.

Prefer Argument Lists Over shell=True

Most subprocess code should pass a list of arguments rather than a shell string.

python
subprocess.run(["git", "status"], check=True)

This is safer and avoids quoting problems. shell=True is only appropriate when you intentionally need shell syntax such as pipes, wildcards, or built-in shell commands.

If you do use shell=True, treat untrusted input as dangerous.

Common Pitfalls

  • Expecting subprocess.call() to return stdout instead of the exit code.
  • Using old APIs when subprocess.run() would be simpler.
  • Forgetting text=True and then wondering why output is bytes.
  • Ignoring stderr and debugging the wrong stream.
  • Using shell=True when a normal argument list would be safer.

Summary

  • 'subprocess.call() returns only the process exit code.'
  • Use subprocess.run() with capture_output=True for most output-capture cases.
  • Use check_output() when you only need stdout.
  • Use Popen when you need streaming or finer control.
  • Prefer argument lists over shell=True unless shell features are truly required.

Related reading
Free course
Beginner
7 lessons
2 hours
Tackling System Design Interview Problems

A short course that equips you with the skills to approach system design interviews methodically.

Start the free course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.