shell=True passes input to the shell, enabling command injection via metacharacters
When shell=True is used, subprocess hands the command string to /bin/sh (or cmd.exe on Windows). The shell interprets metacharacters such as ;, |, &&, $(), and backticks. If any part of the command comes from user input, an attacker can break out of the intended command and execute arbitrary commands with the privileges of the Python process. Even without shell=True, argument injection is possible if user input starts with a dash and is interpreted as a flag by the target program. The safe pattern is shell=False with a list of arguments, which passes each argument directly to execve without shell parsing. If you must use a shell, quote each argument with shlex.quote and validate against an allowlist of allowed characters or values. Never interpolate untrusted input into a shell command. Also avoid shell=True when the command is static, because it adds a shell process and platform-specific behavior for no benefit.
shell=False with a list: the program receives arguments exactly as given, with no shell interpretation.
shell=True: the string is parsed by the shell, so ;, |, &&, $(), and backticks are dangerous.
Argument injection: even with shell=False, a value starting with '-' can be treated as an option by the target program.
shlex.quote can be used when shell=True is unavoidable, but allowlisting is safer.
Common mistake: using shell=True with an f-string that includes user input. This is command injection.
Common mistake: assuming that escaping spaces is enough. Shell metacharacters are the real risk.
Version note: subprocess.run is the recommended modern API since 3.5. Older call, check_output, and Popen still work but are not preferred for new code.
0-2 years experience
2-5 years experience
5-8 years experience
8+ years experience