CertGrid CertGrid
Hands-on Lab·Python Automation for IT

Python subprocess Without a Shell

Automation spends a lot of its time running other programs. subprocess.run with a list of arguments does that with no shell involved, which means no quoting, no glob expansion and no injection. shell=True hands your string to /bin/sh instead - and this guide shows exactly what that costs, with a captured command injection that removes a file it was never asked to touch.

OS and Process Automation Guide 17 of 39 Intermediate

subprocess.run has been the recommended interface since Python 3.5, and capture_output=True since 3.7. os.system and the old commands module are gone or should be treated as such.

Everything on this page runs on the control node. Any machine with Python 3 will do.
Server NameIP AddressOSRolesCPURAMHDD
RUNNER01192.168.0.27Ubuntu 26.04 LTSControl node - every script in this path runs here2 Core4 GB50 GB

This guide includes

Use this because automation spends much of its time running other programs. This matters because shell=True turns a hostname read from a config file into a security decision - and passing a list of arguments avoids it entirely.

Before you start

  1. The one call worth remembering

  2. A list, not a string

  3. What a shell would have done to that

  4. And why shell=True is a security decision

  5. The command that does not exist

  6. A pipeline, without a shell

  7. shlex, when a command really does arrive as a string

Official sources