Drill 02: File Operations
Read, write, JSON, CSV, and modern pathlib patterns.
Run This Drill
bash ~/atelier/_bibliotheca/domus-captures/docs/modules/ROOT/examples/python-drills/02-file-ops.sh
Drill Script
#!/bin/bash
# PYTHON DRILL 02: FILE OPERATIONS
# Paste this entire script into your terminal
# Topics: Reading, writing, JSON, CSV, Path
# Create test files
cat << 'EOF' > /tmp/servers.txt
ise-01,10.50.1.20,443
ise-02,10.50.1.21,443
bind-01,10.50.1.90,53
vault-01,10.50.1.132,8200
EOF
cat << 'EOF' > /tmp/config.json
{
"hostname": "ise-01",
"ip": "10.50.1.20",
"port": 443,
"enabled": true,
"roles": ["pan", "mnt"]
}
EOF
echo "=================================================================="
echo " PYTHON DRILL 02: FILE OPERATIONS "
echo "=================================================================="
echo ""
echo "Test files created: /tmp/servers.txt, /tmp/config.json"
echo ""
# ---------------------------------------------------------------------------
echo "------------------------------------------------------------------"
echo "DRILL 2.1: READING TEXT FILES"
echo "Three ways to read files"
echo "------------------------------------------------------------------"
echo ""
python3 << 'PYEOF'
# Method 1: Read entire file
with open("/tmp/servers.txt") as f:
content = f.read()
print("=== Read entire file ===")
print(content)
# Method 2: Read as list of lines
with open("/tmp/servers.txt") as f:
lines = f.readlines()
print(f"=== As list ({len(lines)} lines) ===")
print(lines)
# Method 3: Iterate (memory efficient)
print("=== Iterate line by line ===")
with open("/tmp/servers.txt") as f:
for line in f:
print(f" {line.strip()}")
PYEOF
echo ""
# ---------------------------------------------------------------------------
echo "------------------------------------------------------------------"
echo "DRILL 2.2: PARSING CSV-LIKE DATA"
echo "Split lines into fields"
echo "------------------------------------------------------------------"
echo ""
python3 << 'PYEOF'
servers = []
with open("/tmp/servers.txt") as f:
for line in f:
hostname, ip, port = line.strip().split(",")
servers.append({
"hostname": hostname,
"ip": ip,
"port": int(port)
})
print("=== Parsed servers ===")
for server in servers:
print(f" {server['hostname']}: {server['ip']}:{server['port']}")
# Filter to specific port
port_443 = [s for s in servers if s["port"] == 443]
print(f"\n=== Port 443 only ===")
for s in port_443:
print(f" {s['hostname']}")
PYEOF
echo ""
# ---------------------------------------------------------------------------
echo "------------------------------------------------------------------"
echo "DRILL 2.3: JSON FILES"
echo "Read, modify, write JSON"
echo "------------------------------------------------------------------"
echo ""
python3 << 'PYEOF'
import json
# Read JSON
with open("/tmp/config.json") as f:
config = json.load(f)
print("=== Read JSON ===")
print(f"Hostname: {config['hostname']}")
print(f"Roles: {config['roles']}")
# Modify
config["status"] = "active"
config["roles"].append("psn")
# Write JSON (pretty)
with open("/tmp/config_updated.json", "w") as f:
json.dump(config, f, indent=2)
print("\n=== Written to /tmp/config_updated.json ===")
with open("/tmp/config_updated.json") as f:
print(f.read())
# JSON string operations
data = {"key": "value", "num": 42}
json_str = json.dumps(data)
print(f"\n=== dumps (to string): {json_str}")
parsed = json.loads(json_str)
print(f"=== loads (from string): {parsed}")
PYEOF
echo ""
# ---------------------------------------------------------------------------
echo "------------------------------------------------------------------"
echo "DRILL 2.4: CSV MODULE"
echo "Proper CSV handling with headers"
echo "------------------------------------------------------------------"
echo ""
python3 << 'PYEOF'
import csv
# Write CSV with headers
servers = [
{"hostname": "web-01", "ip": "10.50.10.101", "port": 80},
{"hostname": "db-01", "ip": "10.50.10.50", "port": 5432},
{"hostname": "cache-01", "ip": "10.50.10.60", "port": 6379}
]
with open("/tmp/inventory.csv", "w", newline="") as f:
writer = csv.DictWriter(f, fieldnames=["hostname", "ip", "port"])
writer.writeheader()
writer.writerows(servers)
print("=== Written /tmp/inventory.csv ===")
# Read CSV with headers
with open("/tmp/inventory.csv") as f:
reader = csv.DictReader(f)
print("\n=== Read back ===")
for row in reader:
print(f" {row['hostname']}: {row['ip']}")
PYEOF
echo ""
# ---------------------------------------------------------------------------
echo "------------------------------------------------------------------"
echo "DRILL 2.5: PATHLIB (MODERN PATH HANDLING)"
echo "Object-oriented filesystem paths"
echo "------------------------------------------------------------------"
echo ""
python3 << 'PYEOF'
from pathlib import Path
# Current directory
cwd = Path.cwd()
print(f"CWD: {cwd}")
# Path manipulation
config_path = Path("/etc/ssh/sshd_config")
print(f"Path: {config_path}")
print(f"Name: {config_path.name}")
print(f"Stem: {config_path.stem}")
print(f"Suffix: {config_path.suffix}")
print(f"Parent: {config_path.parent}")
print(f"Exists: {config_path.exists()}")
# Joining paths
base = Path("/home/user")
full = base / "projects" / "infra"
print(f"\nJoined: {full}")
# Glob for files
tmp = Path("/tmp")
txt_files = list(tmp.glob("*.txt"))
print(f"\n*.txt in /tmp: {[f.name for f in txt_files[:5]]}")
# Read/write with pathlib
p = Path("/tmp/test_pathlib.txt")
p.write_text("Hello from pathlib!\n")
print(f"\nRead back: {p.read_text()}")
PYEOF
echo ""
# ---------------------------------------------------------------------------
echo "------------------------------------------------------------------"
echo "DRILL 2.6: ERROR HANDLING"
echo "Graceful file operation failures"
echo "------------------------------------------------------------------"
echo ""
python3 << 'PYEOF'
from pathlib import Path
# Try to read non-existent file
try:
with open("/tmp/nonexistent.txt") as f:
content = f.read()
except FileNotFoundError:
print("File not found - handled gracefully")
# Check before reading
path = Path("/tmp/maybe_exists.txt")
if path.exists():
content = path.read_text()
else:
print(f"{path} does not exist")
# Handle JSON parse errors
import json
bad_json = "{ invalid json }"
try:
data = json.loads(bad_json)
except json.JSONDecodeError as e:
print(f"JSON parse error: {e}")
PYEOF
echo ""
# ---------------------------------------------------------------------------
echo "------------------------------------------------------------------"
echo "YOUR TURN - TRY THESE:"
echo "------------------------------------------------------------------"
echo ""
echo "1. Read /etc/passwd first 5 lines:"
echo " with open('/etc/passwd') as f:"
echo " for i, line in enumerate(f):"
echo " if i >= 5: break"
echo " print(line.strip())"
echo ""
echo "2. Write JSON to file:"
echo " import json"
echo " data = {'hosts': ['web', 'db']}"
echo " Path('/tmp/hosts.json').write_text(json.dumps(data, indent=2))"
echo ""
echo "3. Find all .sh files in /tmp:"
echo " from pathlib import Path"
echo " list(Path('/tmp').glob('*.sh'))"
echo ""
echo "------------------------------------------------------------------"
echo "KEY TAKEAWAYS:"
echo "1. Always use 'with open()' for auto-close"
echo "2. json.load(file) / json.dump(data, file)"
echo "3. json.loads(string) / json.dumps(data)"
echo "4. csv.DictReader/DictWriter for headers"
echo "5. pathlib.Path for modern path handling"
echo "------------------------------------------------------------------"