Python subprocess replacement that allows calling system commands as Python functions
npx @tessl/cli install tessl/pypi-sh@2.2.0A 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.
pip install shimport shFor specific commands:
from sh import ls, git, docker # Any system commandFor classes and utilities:
from sh import Command, pushd, glob, ErrorReturnCodeFor contrib commands:
from sh.contrib import git, sudo, bash # Enhanced command versionsimport sh
# Call any system command as a Python function
output = sh.ls("-la", "/tmp")
print(output)
# Chain commands with pipes
result = sh.grep(sh.ps("aux"), "python")
# Run commands in the background
proc = sh.sleep(10, _bg=True)
print("Command running in background...")
proc.wait()
# Handle command output in real-time
def process_line(line):
print(f"Output: {line.strip()}")
sh.tail("-f", "/var/log/system.log", _out=process_line)
# Change directory temporarily
with sh.pushd("/tmp"):
files = sh.ls() # lists files in /tmp
print(files)
# Back to original directory
# Handle errors
try:
sh.ls("/nonexistent")
except sh.ErrorReturnCode_2 as e:
print(f"Command failed: {e}")The sh library uses a dynamic command resolution system built around these core components:
sh.ls) creates Command objects for system programsThis design enables treating shell commands as first-class Python objects while maintaining full control over process execution, I/O redirection, and error handling.
Core functionality for executing system commands with comprehensive process control, argument handling, and execution modes including foreground, background, and interactive execution.
def __call__(*args, **kwargs): ... # Command execution
def bake(*args, **kwargs): ... # Pre-configure command argumentsAdvanced process control including background execution, process monitoring, signal handling, and process lifecycle management with support for long-running commands.
def wait(): ... # Wait for background process completion
def kill(): ... # Terminate running process
def terminate(): ... # Gracefully terminate process
def is_alive(): ... # Check if process is runningComprehensive I/O redirection and streaming capabilities including real-time output processing, piping between commands, input feeding, and output capturing with multiple formats.
def _out(callback): ... # Real-time output processing
def _err(callback): ... # Real-time error processing
def _in(data): ... # Feed input to command
def _piped: ... # Enable piping to other commandsRobust error handling system with specific exception classes for different failure modes, exit code management, and comprehensive error information capture.
class ErrorReturnCode(Exception): ...
class ErrorReturnCode_1(ErrorReturnCode): ... # Exit code 1
class SignalException(Exception): ...
class TimeoutException(Exception): ...
class CommandNotFound(Exception): ...Enhanced command wrappers that provide optimized defaults and specialized functionality for common tools like git, sudo, bash, and ssh.
@contrib("git")
def git(orig): ... # Git with optimized defaults
@contrib("sudo")
def sudo(orig): ... # Sudo with password handling
@contrib("bash")
def bash(orig): ... # Bash with -c flag pre-configuredAdditional utilities including directory manipulation, enhanced globbing, logging, and command introspection tools for advanced shell integration scenarios.
def pushd(path): ... # Directory context manager
def glob(pattern): ... # Enhanced glob with sh integration
class Logger: ... # Command execution loggerclass Command:
"""Represents an un-run system program."""
def __init__(self, path: str, search_paths=None): ...
def __call__(self, *args, **kwargs): ... # Returns str or RunningCommand
def bake(self, *args, **kwargs): ... # Returns new Command
def __str__(self) -> str: ... # String representation with path
def __repr__(self) -> str: ... # Formal string representation
def __eq__(self, other) -> bool: ... # Equality comparison
class RunningCommand:
"""Represents an executing command process."""
def wait(self): ...
def kill(self): ...
def terminate(self): ...
def signal(self, sig): ... # Send signal to process
def is_alive(self) -> bool: ...
@property
def pid(self) -> int: ... # Process ID
@property
def stdout(self) -> str: ...
@property
def stderr(self) -> str: ...
@property
def exit_code(self) -> int: ...
@property
def process(self): ... # Access to subprocess.Popen object
class ErrorReturnCode(Exception):
"""Base exception for command execution errors."""
def __init__(self, full_cmd: str, stdout: bytes, stderr: bytes): ...
@property
def exit_code(self) -> int: ...
@property
def full_cmd(self) -> str: ... # Complete command executed
@property
def stdout(self) -> bytes: ... # Command stdout output
@property
def stderr(self) -> bytes: ... # Command stderr output
class SignalException(Exception):
"""Exception raised when process is terminated by a signal."""
def __init__(self, full_cmd: str, signal_code: int): ...
class TimeoutException(Exception):
"""Exception raised when command times out."""
def __init__(self, full_cmd: str, timeout: float): ...
class CommandNotFound(Exception):
"""Exception raised when command cannot be found in PATH."""
def __init__(self, command: str): ...
class ForkException(Exception):
"""Exception raised when there's an error in the fork process."""
def __init__(self, command: str, error: str): ...
class Logger:
"""Memory-efficient command execution logger."""
def __init__(self, name: str, context: dict = None): ...
def log(self, level: str, message: str): ...
def info(self, message: str): ...
def debug(self, message: str): ...
def warning(self, message: str): ...
def error(self, message: str): ...
class GlobResults(list):
"""Enhanced list of glob results with additional methods."""
def __init__(self, results): ...
class StreamBufferer:
"""Advanced: Internal buffering implementation for I/O streams."""
def __init__(self, buffer_size: int = 0, encoding: str = 'utf-8'): ...