Files
gh-basher83-lunar-claude-pl…/skills/python-uv-scripts/reference/bash-to-python.md
2025-11-29 18:00:18 +08:00

294 lines
5.3 KiB
Markdown

# Converting Bash Scripts to Python uv Scripts
This document guides the conversion of bash scripts to Python uv scripts.
## When Conversion Makes Sense
### ✅ Good Candidates for Conversion
**Convert when**:
- Script needs better error handling
- Cross-platform compatibility required
- Complex data processing needed
- API interactions involved
- Script will grow in complexity
**Examples**:
```bash
# Good candidate - API interaction
curl -X POST https://api.example.com/data \
-H "Authorization: Bearer $TOKEN" \
-d '{"status": "active"}'
# Good candidate - Data processing
cat data.json | jq '.users[] | select(.active == true)'
# Good candidate - Complex logic
for file in $(find /data -name "*.log"); do
count=$(grep -c "ERROR" "$file")
if [ $count -gt 100 ]; then
# Complex processing
fi
done
```
### ❌ Keep as Bash When
**Don't convert when**:
- Simple file operations (cp, mv, mkdir)
- Shell-specific features heavily used (job control, pipes)
- System administration tasks
- Script is <10 lines and works fine
- Team primarily knows bash
**Examples**:
```bash
# Keep as bash - Simple operations
#!/bin/bash
mkdir -p /var/log/app
cp config.yaml /etc/app/
# Keep as bash - Shell-specific
#!/bin/bash
find /data -name "*.tmp" -mtime +7 -delete
# Keep as bash - System admin
#!/bin/bash
systemctl restart nginx
journalctl -u nginx -f
```
## Common Conversions
### File Operations
**Bash**:
```bash
#!/bin/bash
if [ -f "/etc/config.yaml" ]; then
cp /etc/config.yaml /backup/
fi
```
**Python**:
```python
#!/usr/bin/env -S uv run --script
# /// script
# requires-python = ">=3.11"
# dependencies = []
# ///
from pathlib import Path
import shutil
config = Path("/etc/config.yaml")
if config.exists():
shutil.copy(config, "/backup/")
```
### Environment Variables
**Bash**:
```bash
#!/bin/bash
API_URL=${API_URL:-"https://api.example.com"}
if [ -z "$API_TOKEN" ]; then
echo "Error: API_TOKEN not set" >&2
exit 1
fi
```
**Python**:
```python
#!/usr/bin/env -S uv run --script
# /// script
# requires-python = ">=3.11"
# dependencies = []
# ///
import os
import sys
API_URL = os.getenv("API_URL", "https://api.example.com")
API_TOKEN = os.getenv("API_TOKEN")
if not API_TOKEN:
print("Error: API_TOKEN not set", file=sys.stderr)
sys.exit(1)
```
### Running Commands
**Bash**:
```bash
#!/bin/bash
set -euo pipefail
output=$(systemctl status nginx)
if [ $? -ne 0 ]; then
echo "Error: nginx not running"
exit 1
fi
```
**Python**:
```python
#!/usr/bin/env -S uv run --script
# /// script
# requires-python = ">=3.11"
# dependencies = []
# ///
import subprocess
import sys
try:
result = subprocess.run(
["systemctl", "status", "nginx"],
capture_output=True,
text=True,
check=True
)
print(result.stdout)
except subprocess.CalledProcessError:
print("Error: nginx not running", file=sys.stderr)
sys.exit(1)
```
### HTTP Requests
**Bash**:
```bash
#!/bin/bash
response=$(curl -s -X GET https://api.github.com/users/octocat)
name=$(echo "$response" | jq -r '.name')
echo "Name: $name"
```
**Python**:
```python
#!/usr/bin/env -S uv run --script
# /// script
# requires-python = ">=3.11"
# dependencies = [
# "httpx>=0.27.0",
# ]
# ///
import httpx
response = httpx.get("https://api.github.com/users/octocat")
response.raise_for_status()
data = response.json()
print(f"Name: {data['name']}")
```
### JSON Processing
**Bash**:
```bash
#!/bin/bash
jq '.users[] | select(.active == true) | .name' data.json
```
**Python**:
```python
#!/usr/bin/env -S uv run --script
# /// script
# requires-python = ">=3.11"
# dependencies = []
# ///
import json
from pathlib import Path
data = json.loads(Path("data.json").read_text())
active_names = [
user["name"]
for user in data["users"]
if user.get("active")
]
print("\n".join(active_names))
```
## Decision Framework
```text
Should I convert this bash script?
├─ Is it >50 lines? → Consider conversion
├─ Does it process JSON/YAML? → Strong candidate
├─ Does it make API calls? → Strong candidate
├─ Does it have complex logic? → Consider conversion
├─ Does it need better error handling? → Consider conversion
├─ Is it mostly shell commands? → Keep as bash
└─ Is it <10 lines and works? → Keep as bash
```
## Hybrid Approach
Sometimes the best solution is calling bash from Python:
```python
#!/usr/bin/env -S uv run --script
# /// script
# requires-python = ">=3.11"
# dependencies = []
# ///
"""
Hybrid: Use Python for logic, bash for system commands.
"""
import subprocess
def backup_with_rsync(source: str, dest: str):
"""Use rsync (better than reimplementing in Python)."""
subprocess.run(
["rsync", "-av", "--delete", source, dest],
check=True
)
# Python logic here
# ...
# Leverage bash tools where appropriate
backup_with_rsync("/data/", "/backup/")
```
## Summary
**Convert to Python when**:
- Complex logic or data processing
- API interactions
- Cross-platform needs
- Better error handling required
- Will grow in complexity
**Keep as Bash when**:
- Simple file operations
- System administration
- Heavily uses shell features
- Works well and won't change
- Team expertise is bash
**Consider Hybrid**:
- Complex Python logic + system commands
- Leverage both Python libraries and shell tools