Command Execution¶
fscm provides flexible command execution with output capture, sudo support, and error handling.
Basic Usage¶
import fscm
# Run a command
fscm.run("echo 'Hello, World!'")
# Capture the result
result = fscm.run("hostname")
print(result.stdout) # The hostname
The RunReturn Object¶
Every run() call returns a RunReturn object:
result = fscm.run("ls -la /tmp")
result.ok # True if exit code is 0
result.returncode # The exit code (0, 1, etc.)
result.stdout # Standard output (str or bytes)
result.stderr # Standard error (str or bytes)
result.to_change # Convert to CmdRun change object
Checking Success¶
result = fscm.run("some-command", check=False)
if result.ok:
print("Success!")
print(result.stdout)
else:
print(f"Failed with code {result.returncode}")
print(result.stderr)
Asserting Success¶
# Raises exception if command fails
result = fscm.run("critical-command")
result.assert_ok() # Explicit assertion
# Or use check=True (default)
fscm.run("critical-command", check=True) # Raises on failure
Parameters¶
| Parameter | Type | Default | Description |
|---|---|---|---|
cmd |
str | required | Command to execute |
check |
bool | True | Raise on non-zero exit |
sudo |
bool | False | Run with sudo |
quiet |
bool | False | Suppress output display |
destructive |
bool | True | Track in change list |
env |
dict | None | Environment variables |
cwd |
str | None | Working directory |
stdin |
str | None | Input to send |
timeout |
int | None | Timeout in seconds |
Using Sudo¶
import fscm
# Run with sudo
fscm.run("systemctl restart nginx", sudo=True)
# Set sudo password globally
fscm.settings.sudo_password = "mypassword"
fscm.run("apt-get update", sudo=True) # Uses the password
Sudo Password Security
Avoid hardcoding passwords. Use environment variables or secrets management:
Output Control¶
Quiet Mode¶
Streaming Output¶
# Stream output in real-time (default)
fscm.settings.stream_output = True
fscm.run("make build") # Output shown as it happens
# Disable streaming
fscm.settings.stream_output = False
Read-Only Commands¶
For commands that don't modify the system:
import fscm
# run_ro() is quiet and non-destructive by default
result = fscm.run_ro("cat /etc/hostname")
print(result.stdout)
# Equivalent to:
result = fscm.run("cat /etc/hostname", quiet=True, destructive=False)
Checking Command Existence¶
import fscm
# Check if a command exists
if fscm.run_ro("which docker").ok:
print("Docker is installed")
# Check if command fails
if fscm.fails("systemctl is-active nginx"):
print("Nginx is not running")
Getting Output as String¶
import fscm
# Get stdout directly as string
hostname = fscm.getstdout("hostname")
print(f"Running on {hostname}")
# With sudo
kernel = fscm.getstdout("uname -r", sudo=True)
Running Multiple Commands¶
import fscm
# Run multiple commands
fscm.runmany("""
apt-get update
apt-get install -y nginx
systemctl enable nginx
""", sudo=True)
# Or as a list
fscm.runmany([
"apt-get update",
"apt-get install -y nginx",
"systemctl enable nginx"
], sudo=True)
Background Execution¶
import fscm
# Run in background
proc = fscm.run_bg("long-running-task")
# Do other work...
# Check if still running
if proc.poll() is None:
print("Still running")
# Wait for completion
proc.wait()
Environment Variables¶
import fscm
# Set environment variables
fscm.run(
"npm install",
env={"NODE_ENV": "production", "CI": "true"}
)
Working Directory¶
import fscm
# Run in specific directory
fscm.run("npm install", cwd="/var/www/myapp")
fscm.run("make build", cwd="/home/user/project")
Providing Input¶
import fscm
# Send input to command
fscm.run("passwd user", stdin="newpassword\nnewpassword\n", sudo=True)
# Pipe content
fscm.run("mysql mydb", stdin="SELECT * FROM users;")
Timeout¶
import fscm
# Set timeout (in seconds)
try:
fscm.run("sleep 100", timeout=5)
except TimeoutError:
print("Command timed out")
Error Handling¶
import fscm
from fscm import CmdFailedException
# Default: raises on failure
try:
fscm.run("false") # Always fails
except CmdFailedException as e:
print(f"Command failed: {e}")
# Disable raising
result = fscm.run("false", check=False)
if not result.ok:
print("Command failed but we handled it")
Destructive vs Non-Destructive¶
import fscm
# Destructive commands are tracked
fscm.run("rm -rf /tmp/build") # Recorded in CHANGELIST
# Non-destructive commands are not tracked
fscm.run("ls -la", destructive=False) # Not recorded
# run_ro() is non-destructive by default
fscm.run_ro("cat /etc/hosts") # Not recorded
Example: Service Management¶
import fscm
def manage_service(name: str, action: str):
"""Manage a systemd service."""
# Check current state
status = fscm.run_ro(f"systemctl is-active {name}")
if action == "start" and not status.ok:
fscm.run(f"systemctl start {name}", sudo=True)
elif action == "stop" and status.ok:
fscm.run(f"systemctl stop {name}", sudo=True)
elif action == "restart":
fscm.run(f"systemctl restart {name}", sudo=True)
def deploy_and_restart():
# Deploy new config
fscm.file("/etc/myapp/config.yaml", new_config)
# Restart only if config changed
if any(c.filename == "/etc/myapp/config.yaml" for c in fscm.CHANGELIST):
manage_service("myapp", "restart")
Example: Build Pipeline¶
import fscm
def build_project(project_dir: str):
"""Build a project with proper error handling."""
# Clean previous build
fscm.run("rm -rf dist/", cwd=project_dir)
# Install dependencies
result = fscm.run("npm ci", cwd=project_dir, check=False)
if not result.ok:
print("Failed to install dependencies")
return False
# Run tests
result = fscm.run("npm test", cwd=project_dir, check=False)
if not result.ok:
print("Tests failed")
return False
# Build
fscm.run("npm run build", cwd=project_dir)
return True
Next Steps¶
- Package Management — Installing packages
- Remote Execution — Running commands remotely