Python subprocess replacement that allows calling system commands as Python functions
npx @tessl/cli install tessl/pypi-sh@2.2.00
# sh
1
2
A comprehensive Python subprocess replacement that allows calling any system command as if it were a Python function. The `sh` library provides a more Pythonic interface to shell commands with advanced process management, piping, background execution, and real-time output streaming for Unix-like systems.
3
4
## Package Information
5
6
- **Package Name**: sh
7
- **Language**: Python
8
- **Installation**: `pip install sh`
9
- **Supported Platforms**: Linux, macOS, BSD (Unix-like systems only)
10
- **Python Versions**: 3.8-3.12, PyPy
11
12
## Core Imports
13
14
```python
15
import sh
16
```
17
18
For specific commands:
19
20
```python
21
from sh import ls, git, docker # Any system command
22
```
23
24
For classes and utilities:
25
26
```python
27
from sh import Command, pushd, glob, ErrorReturnCode
28
```
29
30
For contrib commands:
31
32
```python
33
from sh.contrib import git, sudo, bash # Enhanced command versions
34
```
35
36
## Basic Usage
37
38
```python
39
import sh
40
41
# Call any system command as a Python function
42
output = sh.ls("-la", "/tmp")
43
print(output)
44
45
# Chain commands with pipes
46
result = sh.grep(sh.ps("aux"), "python")
47
48
# Run commands in the background
49
proc = sh.sleep(10, _bg=True)
50
print("Command running in background...")
51
proc.wait()
52
53
# Handle command output in real-time
54
def process_line(line):
55
print(f"Output: {line.strip()}")
56
57
sh.tail("-f", "/var/log/system.log", _out=process_line)
58
59
# Change directory temporarily
60
with sh.pushd("/tmp"):
61
files = sh.ls() # lists files in /tmp
62
print(files)
63
# Back to original directory
64
65
# Handle errors
66
try:
67
sh.ls("/nonexistent")
68
except sh.ErrorReturnCode_2 as e:
69
print(f"Command failed: {e}")
70
```
71
72
## Architecture
73
74
The sh library uses a dynamic command resolution system built around these core components:
75
76
- **Command**: Represents an un-run system program that can be configured and executed
77
- **RunningCommand**: Manages executing processes with advanced I/O handling and control
78
- **Dynamic Resolution**: Any attribute access (like `sh.ls`) creates Command objects for system programs
79
- **Exception Hierarchy**: Specific exception classes for different exit codes and signals
80
- **Process Management**: Background execution, real-time streaming, piping, and signal handling
81
82
This design enables treating shell commands as first-class Python objects while maintaining full control over process execution, I/O redirection, and error handling.
83
84
## Capabilities
85
86
### Command Execution
87
88
Core functionality for executing system commands with comprehensive process control, argument handling, and execution modes including foreground, background, and interactive execution.
89
90
```python { .api }
91
def __call__(*args, **kwargs): ... # Command execution
92
def bake(*args, **kwargs): ... # Pre-configure command arguments
93
```
94
95
[Command Execution](./command-execution.md)
96
97
### Process Management
98
99
Advanced process control including background execution, process monitoring, signal handling, and process lifecycle management with support for long-running commands.
100
101
```python { .api }
102
def wait(): ... # Wait for background process completion
103
def kill(): ... # Terminate running process
104
def terminate(): ... # Gracefully terminate process
105
def is_alive(): ... # Check if process is running
106
```
107
108
[Process Management](./process-management.md)
109
110
### Input/Output Handling
111
112
Comprehensive I/O redirection and streaming capabilities including real-time output processing, piping between commands, input feeding, and output capturing with multiple formats.
113
114
```python { .api }
115
def _out(callback): ... # Real-time output processing
116
def _err(callback): ... # Real-time error processing
117
def _in(data): ... # Feed input to command
118
def _piped: ... # Enable piping to other commands
119
```
120
121
[Input/Output Handling](./io-handling.md)
122
123
### Error Handling
124
125
Robust error handling system with specific exception classes for different failure modes, exit code management, and comprehensive error information capture.
126
127
```python { .api }
128
class ErrorReturnCode(Exception): ...
129
class ErrorReturnCode_1(ErrorReturnCode): ... # Exit code 1
130
class SignalException(Exception): ...
131
class TimeoutException(Exception): ...
132
class CommandNotFound(Exception): ...
133
```
134
135
[Error Handling](./error-handling.md)
136
137
### Contrib Commands
138
139
Enhanced command wrappers that provide optimized defaults and specialized functionality for common tools like git, sudo, bash, and ssh.
140
141
```python { .api }
142
@contrib("git")
143
def git(orig): ... # Git with optimized defaults
144
@contrib("sudo")
145
def sudo(orig): ... # Sudo with password handling
146
@contrib("bash")
147
def bash(orig): ... # Bash with -c flag pre-configured
148
```
149
150
[Contrib Commands](./contrib.md)
151
152
### Utilities and Helpers
153
154
Additional utilities including directory manipulation, enhanced globbing, logging, and command introspection tools for advanced shell integration scenarios.
155
156
```python { .api }
157
def pushd(path): ... # Directory context manager
158
def glob(pattern): ... # Enhanced glob with sh integration
159
class Logger: ... # Command execution logger
160
```
161
162
[Utilities](./utilities.md)
163
164
## Types
165
166
```python { .api }
167
class Command:
168
"""Represents an un-run system program."""
169
def __init__(self, path: str, search_paths=None): ...
170
def __call__(self, *args, **kwargs): ... # Returns str or RunningCommand
171
def bake(self, *args, **kwargs): ... # Returns new Command
172
def __str__(self) -> str: ... # String representation with path
173
def __repr__(self) -> str: ... # Formal string representation
174
def __eq__(self, other) -> bool: ... # Equality comparison
175
176
class RunningCommand:
177
"""Represents an executing command process."""
178
def wait(self): ...
179
def kill(self): ...
180
def terminate(self): ...
181
def signal(self, sig): ... # Send signal to process
182
def is_alive(self) -> bool: ...
183
@property
184
def pid(self) -> int: ... # Process ID
185
@property
186
def stdout(self) -> str: ...
187
@property
188
def stderr(self) -> str: ...
189
@property
190
def exit_code(self) -> int: ...
191
@property
192
def process(self): ... # Access to subprocess.Popen object
193
194
class ErrorReturnCode(Exception):
195
"""Base exception for command execution errors."""
196
def __init__(self, full_cmd: str, stdout: bytes, stderr: bytes): ...
197
@property
198
def exit_code(self) -> int: ...
199
@property
200
def full_cmd(self) -> str: ... # Complete command executed
201
@property
202
def stdout(self) -> bytes: ... # Command stdout output
203
@property
204
def stderr(self) -> bytes: ... # Command stderr output
205
206
class SignalException(Exception):
207
"""Exception raised when process is terminated by a signal."""
208
def __init__(self, full_cmd: str, signal_code: int): ...
209
210
class TimeoutException(Exception):
211
"""Exception raised when command times out."""
212
def __init__(self, full_cmd: str, timeout: float): ...
213
214
class CommandNotFound(Exception):
215
"""Exception raised when command cannot be found in PATH."""
216
def __init__(self, command: str): ...
217
218
class ForkException(Exception):
219
"""Exception raised when there's an error in the fork process."""
220
def __init__(self, command: str, error: str): ...
221
222
class Logger:
223
"""Memory-efficient command execution logger."""
224
def __init__(self, name: str, context: dict = None): ...
225
def log(self, level: str, message: str): ...
226
def info(self, message: str): ...
227
def debug(self, message: str): ...
228
def warning(self, message: str): ...
229
def error(self, message: str): ...
230
231
class GlobResults(list):
232
"""Enhanced list of glob results with additional methods."""
233
def __init__(self, results): ...
234
235
class StreamBufferer:
236
"""Advanced: Internal buffering implementation for I/O streams."""
237
def __init__(self, buffer_size: int = 0, encoding: str = 'utf-8'): ...
238
```