sh: Subprocess Wrapper for Unix Programs in Python
Python process launching
At a glance
- What is it?
- sh is a Python module that lets you call external programs as if they were Python functions. Instead of using subprocess and string parsing, you call the program's name and pass arguments as Python arguments. Returns output as a string you can chain, filter, or pass forward.
- Who is it for?
- Use sh if you call external programs often from Python and want a cleaner syntax than subprocess. Do not use it if you are on Windows or if you need detailed control over process internals (stdout/stderr separation, return codes, file descriptors).
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 66 days ago.
- What is it written in?
- Mainly Python, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 29, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Calling Unix programs as Python functions
sh is a wrapper around Python's subprocess module that makes calling external programs feel like calling Python functions. Instead of writing subprocess.run() or subprocess.Popen() with complex string parsing, you import the program name and call it directly. It captures stdout, makes return values accessible as strings, and chains operations naturally. sh is useful when your code frequently shells out to tools like git, docker, curl, or grep, or when you build scripts that orchestrate multiple external programs. It reduces boilerplate and makes the code closer to what you would write in bash.
Calling programs as functions
Import the program you want to call and invoke it as a function. sh finds the program on your PATH and runs it. Arguments are passed as Python function arguments, not as a command-line string. The result is an object that prints as a string and can be iterated line by line:
from sh import git
print(git("status", "--short"))You can pipe commands together using the pipe operator, passing the output of one program to the input of the next. The pipe operator chains programs together, which is more readable and safer than building shell strings by hand. Output from one program becomes the input to the next, just as in shell scripting.
Return values and string output
When you call a program, sh returns an object that behaves like a string. You can print it, slice it, search it with regular expressions, or iterate over its lines. The object holds the captured stdout and newlines are preserved, so you can iterate line by line or manipulate the output as text. If a program exits with a non-zero return code, sh raises ErrorReturnCode by default. You can catch it and inspect the exit code and output. This behavior can be overridden per call if you want to ignore exit codes.
Key constraints and design limits
sh only works on Unix-like systems: Linux, macOS, and BSD variants. It relies on Unix system calls and process models that do not exist on Windows. If your code needs to run on Windows, use the standard subprocess module. sh does not separate stdout and stderr by default; both are captured together. If you need to handle them separately, you must use subprocess directly. sh does not support complex process control like job control or terminal interaction. For long-running processes that need real-time output or interactive input, subprocess may be more appropriate. Performance is comparable to subprocess since sh wraps it, but the overhead of capturing and parsing output adds latency.
Installation and requirements
Install sh from PyPI:
pip install shsh requires Python 3.10 or later, up to 3.14, and supports PyPy. No other dependencies are needed. The module is a single file in the standard distribution, so installation is quick. Development uses tox for testing across Python versions. To run the test suite yourself:
make testTo run a single test:
make test='FunctionalTests.test_background' test_oneDocumentation is built with Sphinx and is available at https://sh.readthedocs.io/.
Comparison with subprocess and os.system
The standard subprocess module gives you fine-grained control: you can handle stdout and stderr separately, set environment variables per call, and control process group behavior. But it requires more boilerplate: you construct command lists, call subprocess.run() or Popen(), and parse output manually. os.system() is simpler but obsolete: it does not capture output easily and has no return value. sh occupies a middle ground: it is simpler than subprocess for common cases (running a program and getting output) but offers less control. Use subprocess if you need that control, os.system() if you are on old Python without subprocess, and sh for straightforward program calls in modern Python.
Chaining and composition
sh excels at chaining commands. The pipe operator (`|`) connects programs naturally, just as in shell scripting. You can chain multiple programs together by piping the output of one into the input of the next. This style is closer to shell scripting than procedural subprocess calls, making scripts easier to understand and maintain. You can also compose operations by storing partial results and piping them to other programs, or by creating reusable command chains that take input and produce output.
Regularly released and Production/Stable status
The last push was on 2026-07-25, showing active maintenance. Recent releases are published regularly: v2.4.0 (July 2026), v2.3.0 (June 2026), and v2.2.6 (June 2026). The project is maintained by Andrew Moffat and is stable (Development Status :: 5 - Production/Stable). It has good test coverage via pytest and tox, and documentation is complete. The MIT license permits use in commercial projects. Support is available via GitHub issues and Stack Overflow.
Editorial conclusion
Use sh if you call external programs often from Python and want a cleaner syntax than subprocess. Do not use it if you are on Windows or if you need detailed control over process internals (stdout/stderr separation, return codes, file descriptors). Before adopting, verify that your target Python version is 3.10 or later and that your deployment environment is Unix-like.
Frequently asked questions
Can you use sh on Windows?
No. sh relies on Unix system calls and process models that exist only on Linux, macOS, and BSD. It will not work on Windows. Use subprocess instead.
How do you handle program errors in sh?
If a program returns a non-zero exit code, sh raises ErrorReturnCode by default. Catch it to inspect the exit code and output. You can also pass `_ok_code=<list>` to any call to allow specific exit codes without raising.
Can you separate stdout and stderr with sh?
By default, sh captures both together. Separating them requires using subprocess directly or writing the output to files. sh prioritizes simplicity over fine-grained stream control.
What is ErrorReturnCode?
ErrorReturnCode is an exception that sh raises when a program exits with a non-zero return code. Catch it to access the exit code (accessed via the exception's code attribute) and the output.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/amoffat-sh)