commit 99185c0a7c66511b528eaddb603fb2e4a894805d Author: Zhongwei Li Date: Sun Nov 30 08:28:57 2025 +0800 Initial commit diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json new file mode 100644 index 0000000..0758b6b --- /dev/null +++ b/.claude-plugin/plugin.json @@ -0,0 +1,15 @@ +{ + "name": "bash-master", + "description": "Expert bash/shell scripting system across ALL platforms with Bash 5.3, 2025 security-first practices, and comprehensive Windows Git Bash/MINGW path conversion guidance. PROACTIVELY activate for: (1) ANY bash/shell script task, (2) Windows Git Bash path conversion issues (MSYS_NO_PATHCONV, cygpath, shell detection), (3) Bash 5.3 features (${ } in-shell substitution, ${| } REPLY syntax, BASH_TRAPSIG), (4) Security-first patterns (60%+ exploits from poor validation, HISTFILE protection, absolute paths), (5) ShellCheck v0.11.0 validation (non-negotiable, POSIX.1-2024), (6) Modern automation (containers/CI/CD/cloud), (7) Scripts under 50 lines (Google Style), (8) Comprehensive debugging (tracing, profiling, breakpoints), (9) Git hooks and pipeline integration, (10) Container-aware scripting, (11) DevOps/deployment automation. Provides: Windows Git Bash complete path conversion guide (automatic conversion, MSYS_NO_PATHCONV, MSYS2_ARG_CONV_EXCL, cygpath, shell detection with $OSTYPE/$MSYSTEM/uname), Claude Code issue #2602 solutions, Bash 5.3 complete features (BASH_TRAPSIG, C23 conformance), ShellCheck v0.11.0 (SC2327/SC2328/SC2294/SC2295), security-first mandatory validation patterns, debugging/troubleshooting techniques, container/Kubernetes detection, cloud provider helpers (AWS/Azure), CI/CD platform integration (GitHub Actions/Azure DevOps), parallel processing patterns, blue-green/canary deployments, Google Shell Style Guide compliance, cross-platform compatibility (Linux/macOS/Windows/containers), POSIX.1-2024 compliance, and production-ready 2025 patterns. Ensures secure, optimized, cloud-native scripts following latest 2025 standards with seamless Windows compatibility.", + "version": "1.5.0", + "author": { + "name": "Josiah Siegel", + "email": "JosiahSiegel@users.noreply.github.com" + }, + "skills": [ + "./skills" + ], + "commands": [ + "./commands" + ] +} \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..2af1d96 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# bash-master + +Expert bash/shell scripting system across ALL platforms with Bash 5.3, 2025 security-first practices, and comprehensive Windows Git Bash/MINGW path conversion guidance. PROACTIVELY activate for: (1) ANY bash/shell script task, (2) Windows Git Bash path conversion issues (MSYS_NO_PATHCONV, cygpath, shell detection), (3) Bash 5.3 features (${ } in-shell substitution, ${| } REPLY syntax, BASH_TRAPSIG), (4) Security-first patterns (60%+ exploits from poor validation, HISTFILE protection, absolute paths), (5) ShellCheck v0.11.0 validation (non-negotiable, POSIX.1-2024), (6) Modern automation (containers/CI/CD/cloud), (7) Scripts under 50 lines (Google Style), (8) Comprehensive debugging (tracing, profiling, breakpoints), (9) Git hooks and pipeline integration, (10) Container-aware scripting, (11) DevOps/deployment automation. Provides: Windows Git Bash complete path conversion guide (automatic conversion, MSYS_NO_PATHCONV, MSYS2_ARG_CONV_EXCL, cygpath, shell detection with $OSTYPE/$MSYSTEM/uname), Claude Code issue #2602 solutions, Bash 5.3 complete features (BASH_TRAPSIG, C23 conformance), ShellCheck v0.11.0 (SC2327/SC2328/SC2294/SC2295), security-first mandatory validation patterns, debugging/troubleshooting techniques, container/Kubernetes detection, cloud provider helpers (AWS/Azure), CI/CD platform integration (GitHub Actions/Azure DevOps), parallel processing patterns, blue-green/canary deployments, Google Shell Style Guide compliance, cross-platform compatibility (Linux/macOS/Windows/containers), POSIX.1-2024 compliance, and production-ready 2025 patterns. Ensures secure, optimized, cloud-native scripts following latest 2025 standards with seamless Windows compatibility. diff --git a/commands/pwsh-script.md b/commands/pwsh-script.md new file mode 100644 index 0000000..160a6f9 --- /dev/null +++ b/commands/pwsh-script.md @@ -0,0 +1,175 @@ +--- +name: pwsh-script +description: Create, review, or optimize bash/shell scripts following 2025 best practices and cross-platform standards +--- + +## 🚨 CRITICAL GUIDELINES + +### Windows File Path Requirements + +**MANDATORY: Always Use Backslashes on Windows for File Paths** + +When using Edit or Write tools on Windows, you MUST use backslashes (`\`) in file paths, NOT forward slashes (`/`). + +**Examples:** +- ❌ WRONG: `D:/repos/project/file.tsx` +- ✅ CORRECT: `D:\repos\project\file.tsx` + +This applies to: +- Edit tool file_path parameter +- Write tool file_path parameter +- All file operations on Windows systems + + +### Documentation Guidelines + +**NEVER create new documentation files unless explicitly requested by the user.** + +- **Priority**: Update existing README.md files rather than creating new documentation +- **Repository cleanliness**: Keep repository root clean - only README.md unless user requests otherwise +- **Style**: Documentation should be concise, direct, and professional - avoid AI-generated tone +- **User preference**: Only create additional .md files when user specifically asks for documentation + + +--- + +# Create/Review Bash Scripts + +## Purpose + +Autonomously create professional, production-ready bash scripts or review/optimize existing scripts following 2025 industry standards. + +## What This Command Does + +**Automatic Actions:** +1. ✅ Creates scripts with mandatory error handling (set -euo pipefail) +2. ✅ Implements ShellCheck-compliant code +3. ✅ Follows Google Shell Style Guide (50-line recommendation) +4. ✅ Adds comprehensive error handling and cleanup +5. ✅ Ensures cross-platform compatibility +6. ✅ Includes proper documentation +7. ✅ Validates security patterns + +**For Reviews:** +- Identifies ShellCheck issues +- Checks for security vulnerabilities +- Validates error handling +- Suggests performance optimizations +- Verifies cross-platform compatibility + +## Usage + +**Create a new script:** +``` +/pwsh-script Create a backup script that archives /data to S3 with error handling +``` + +**Review existing script:** +``` +/pwsh-script Review backup.sh for security issues and best practices +``` + +**Optimize performance:** +``` +/pwsh-script Optimize deploy.sh for better performance +``` + +## What You'll Get + +### New Scripts Include: +- `#!/usr/bin/env bash` shebang +- Safety settings (set -euo pipefail, IFS=$'\n\t') +- Proper function structure +- Input validation +- Error handling with trap +- Usage/help text +- Logging capabilities +- Cross-platform considerations +- ShellCheck compliance + +### Reviews Provide: +- ShellCheck validation results +- Security vulnerability assessment +- Anti-pattern identification +- Performance improvement suggestions +- Cross-platform compatibility notes +- Best practice recommendations + +## Example Output + +```bash +#!/usr/bin/env bash +# +# backup.sh - Archive data to S3 with error handling +# Version: 1.0.0 + +set -euo pipefail +IFS=$'\n\t' + +readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +readonly SCRIPT_NAME="$(basename "${BASH_SOURCE[0]}")" + +# Cleanup on exit +cleanup() { + local exit_code=$? + [[ -n "${TEMP_DIR:-}" ]] && rm -rf "$TEMP_DIR" + exit "$exit_code" +} +trap cleanup EXIT INT TERM + +# Main function +main() { + local source_dir="${1:?Source directory required}" + local s3_bucket="${2:?S3 bucket required}" + + # Validate source directory + if [[ ! -d "$source_dir" ]]; then + echo "Error: Source directory not found: $source_dir" >&2 + return 1 + fi + + # Create archive + local archive_name="backup-$(date +%Y%m%d-%H%M%S).tar.gz" + tar -czf "$archive_name" -C "$source_dir" . || { + echo "Error: Failed to create archive" >&2 + return 1 + } + + # Upload to S3 + aws s3 cp "$archive_name" "s3://$s3_bucket/" || { + echo "Error: Failed to upload to S3" >&2 + rm -f "$archive_name" + return 1 + } + + # Cleanup + rm -f "$archive_name" + echo "Backup completed successfully" +} + +main "$@" +``` + +## 2025 Standards Applied + +- **ShellCheck validation** - Zero warnings +- **Google Style Guide** - Modular functions under 50 lines +- **Modern error handling** - errexit, nounset, pipefail trio +- **Security hardening** - Input validation, path sanitization +- **Cross-platform** - Works on Linux/macOS/Windows (Git Bash/WSL) +- **Production-ready** - Proper cleanup, logging, exit codes + +## When To Use + +- Creating new bash scripts for any purpose +- Automating system tasks +- DevOps/CI/CD pipeline scripts +- Build and deployment automation +- Reviewing security of existing scripts +- Optimizing script performance +- Debugging script issues +- Converting manual commands to automated scripts + +--- + +**After running this command, you'll have production-ready, secure, optimized bash scripts following all 2025 best practices.** diff --git a/plugin.lock.json b/plugin.lock.json new file mode 100644 index 0000000..e90f876 --- /dev/null +++ b/plugin.lock.json @@ -0,0 +1,93 @@ +{ + "$schema": "internal://schemas/plugin.lock.v1.json", + "pluginId": "gh:JosiahSiegel/claude-code-marketplace:plugins/bash-master", + "normalized": { + "repo": null, + "ref": "refs/tags/v20251128.0", + "commit": "94c025e6de48870280201267b4ed48b728bae356", + "treeHash": "648b8cc554d153ed52eb60a82a9e36da69abd48d900e86e738df9c7087eef34c", + "generatedAt": "2025-11-28T10:11:49.431406Z", + "toolVersion": "publish_plugins.py@0.2.0" + }, + "origin": { + "remote": "git@github.com:zhongweili/42plugin-data.git", + "branch": "master", + "commit": "aa1497ed0949fd50e99e70d6324a29c5b34f9390", + "repoRoot": "/Users/zhongweili/projects/openmind/42plugin-data" + }, + "manifest": { + "name": "bash-master", + "description": "Expert bash/shell scripting system across ALL platforms with Bash 5.3, 2025 security-first practices, and comprehensive Windows Git Bash/MINGW path conversion guidance. PROACTIVELY activate for: (1) ANY bash/shell script task, (2) Windows Git Bash path conversion issues (MSYS_NO_PATHCONV, cygpath, shell detection), (3) Bash 5.3 features (${ } in-shell substitution, ${| } REPLY syntax, BASH_TRAPSIG), (4) Security-first patterns (60%+ exploits from poor validation, HISTFILE protection, absolute paths), (5) ShellCheck v0.11.0 validation (non-negotiable, POSIX.1-2024), (6) Modern automation (containers/CI/CD/cloud), (7) Scripts under 50 lines (Google Style), (8) Comprehensive debugging (tracing, profiling, breakpoints), (9) Git hooks and pipeline integration, (10) Container-aware scripting, (11) DevOps/deployment automation. Provides: Windows Git Bash complete path conversion guide (automatic conversion, MSYS_NO_PATHCONV, MSYS2_ARG_CONV_EXCL, cygpath, shell detection with $OSTYPE/$MSYSTEM/uname), Claude Code issue #2602 solutions, Bash 5.3 complete features (BASH_TRAPSIG, C23 conformance), ShellCheck v0.11.0 (SC2327/SC2328/SC2294/SC2295), security-first mandatory validation patterns, debugging/troubleshooting techniques, container/Kubernetes detection, cloud provider helpers (AWS/Azure), CI/CD platform integration (GitHub Actions/Azure DevOps), parallel processing patterns, blue-green/canary deployments, Google Shell Style Guide compliance, cross-platform compatibility (Linux/macOS/Windows/containers), POSIX.1-2024 compliance, and production-ready 2025 patterns. Ensures secure, optimized, cloud-native scripts following latest 2025 standards with seamless Windows compatibility.", + "version": "1.5.0" + }, + "content": { + "files": [ + { + "path": "README.md", + "sha256": "1168a2d1a0ac9bf1d5b35d9322801ff86570d449aefe694c568b213e69cbe2db" + }, + { + "path": ".claude-plugin/plugin.json", + "sha256": "deb1bff2d01bb97ac62d0a860eb4b0cb0e29de7dc3cd78f68fbd9e73fd9fd436" + }, + { + "path": "commands/pwsh-script.md", + "sha256": "087023e0038885675c4422e24177e16fa560ba96d17581c336b41eca8f976cca" + }, + { + "path": "skills/debugging-troubleshooting-2025.md", + "sha256": "660004bf1b8f375e16498331523ca9da6b7d51cf83bd01c8bb5eea40326eac96" + }, + { + "path": "skills/shellcheck-cicd-2025.md", + "sha256": "a272773876c77718fe605249c48940f2ad02de6f3943d4bbe90074f7aeeffccf" + }, + { + "path": "skills/modern-automation-patterns.md", + "sha256": "a1683180d5e4850246b4875ece1fc390b0a4be7c22aeb2ed9e74d08db905588e" + }, + { + "path": "skills/security-first-2025.md", + "sha256": "b5c4ca4ad4a45c41e5723117b8a50457936526133e99306a64b599446c8bd1ef" + }, + { + "path": "skills/bash-53-features.md", + "sha256": "4cf0e6c539d006dcf8a826f2d71a42823f2310f387b0a9816413e0c9fd5d9e98" + }, + { + "path": "skills/bash-master/SKILL.md", + "sha256": "f4da656f3a741d15d3a20db1ff4e25e098fe7311242bb7acbbdc6ad9e923c21f" + }, + { + "path": "skills/bash-master/references/resources.md", + "sha256": "1f5844996294b71fd25ca579bbe66f307b2799e8369a48b2f310096baece6bb8" + }, + { + "path": "skills/bash-master/references/patterns_antipatterns.md", + "sha256": "005d25d1708a26abed44a5feafe69816c79cfed691be5d5a27b80dbcb495137d" + }, + { + "path": "skills/bash-master/references/platform_specifics.md", + "sha256": "6c95018d53c48c0c00f57826e1e12b7a123a6ff62560fcd72b9fb7055e79f480" + }, + { + "path": "skills/bash-master/references/windows-git-bash-paths.md", + "sha256": "2978b6038cbe05599d78390810035eda999877866ce67798839691c62e67939d" + }, + { + "path": "skills/bash-master/references/best_practices.md", + "sha256": "761361d3d9db2f54c51d8cbdd77c4de40cafbe4df4c4f2c14246337a4a1c7814" + }, + { + "path": "skills/bash-master/references/platform_specifics.md.backup", + "sha256": "e8ca9e33f75c16df540e9a3cbc5837041e50e0f382450d417a79c0fb7abb40e6" + } + ], + "dirSha256": "648b8cc554d153ed52eb60a82a9e36da69abd48d900e86e738df9c7087eef34c" + }, + "security": { + "scannedAt": null, + "scannerVersion": null, + "flags": [] + } +} \ No newline at end of file diff --git a/skills/bash-53-features.md b/skills/bash-53-features.md new file mode 100644 index 0000000..49ffa0a --- /dev/null +++ b/skills/bash-53-features.md @@ -0,0 +1,470 @@ +--- +name: bash-53-features +description: Bash 5.3 new features and modern patterns (2025) +--- + +## 🚨 CRITICAL GUIDELINES + +### Windows File Path Requirements + +**MANDATORY: Always Use Backslashes on Windows for File Paths** + +When using Edit or Write tools on Windows, you MUST use backslashes (`\`) in file paths, NOT forward slashes (`/`). + +**Examples:** +- ❌ WRONG: `D:/repos/project/file.tsx` +- ✅ CORRECT: `D:\repos\project\file.tsx` + +This applies to: +- Edit tool file_path parameter +- Write tool file_path parameter +- All file operations on Windows systems + + +### Documentation Guidelines + +**NEVER create new documentation files unless explicitly requested by the user.** + +- **Priority**: Update existing README.md files rather than creating new documentation +- **Repository cleanliness**: Keep repository root clean - only README.md unless user requests otherwise +- **Style**: Documentation should be concise, direct, and professional - avoid AI-generated tone +- **User preference**: Only create additional .md files when user specifically asks for documentation + + +--- + +# Bash 5.3 Features (2025) + +## Overview + +Bash 5.3 (released July 2025) introduces significant new features that improve performance, readability, and functionality. + +## Key New Features + +### 1. In-Shell Command Substitution + +**New: ${ command; } syntax** - Executes without forking a subshell (runs in current shell context): + +```bash +# OLD way (Bash < 5.3) - Creates subshell +output=$(expensive_command) + +# NEW way (Bash 5.3+) - Runs in current shell, faster +output=${ expensive_command; } +``` + +**Benefits:** +- No subshell overhead (faster) +- Preserves variable scope +- Better performance in loops + +**Example:** +```bash +#!/usr/bin/env bash + +# Traditional approach +count=0 +for file in *.txt; do + lines=$(wc -l < "$file") # Subshell created + ((count += lines)) +done + +# Bash 5.3 approach (faster) +count=0 +for file in *.txt; do + lines=${ wc -l < "$file"; } # No subshell + ((count += lines)) +done +``` + +### 2. REPLY Variable Command Substitution + +**New: ${| command; } syntax** - Stores result in REPLY variable: + +```bash +# Runs command, result goes to $REPLY automatically +${| complex_calculation; } +echo "Result: $REPLY" + +# Multiple operations +${| + local_var="processing" + echo "$local_var: $((42 * 2))" +} +echo "Got: $REPLY" +``` + +**Use Cases:** +- Avoid variable naming conflicts +- Clean syntax for temporary values +- Standardized result variable + +### 3. Enhanced `read` Builtin + +**New: -E option** - Uses readline with programmable completion: + +```bash +# Interactive input with tab completion +read -E -p "Enter filename: " filename +# User can now tab-complete file paths! + +# With custom completion +read -E -p "Select environment: " env +# Enables full readline features (history, editing) +``` + +**Benefits:** +- Better UX for interactive scripts +- Built-in path completion +- Command history support + +### 4. Enhanced `source` Builtin + +**New: -p PATH option** - Custom search path for sourcing: + +```bash +# OLD way +source /opt/myapp/lib/helpers.sh + +# NEW way - Search custom path +source -p /opt/myapp/lib:/usr/local/lib helpers.sh + +# Respects CUSTOM_PATH instead of current directory +CUSTOM_PATH=/app/modules:/shared/lib +source -p "$CUSTOM_PATH" database.sh +``` + +**Benefits:** +- Modular library organization +- Avoid hard-coded paths +- Environment-specific sourcing + +### 5. Enhanced `compgen` Builtin + +**New: Variable output option** - Store completions in variable: + +```bash +# OLD way - Output to stdout +completions=$(compgen -f) + +# NEW way - Directly to variable +compgen -v completions_var -f +# Results now in $completions_var +``` + +**Benefits:** +- Cleaner completion handling +- No extra subshells +- Better performance + +### 6. GLOBSORT Variable + +**New: Control glob sorting behavior**: + +```bash +# Default: alphabetical sort +echo *.txt + +# Sort by modification time (newest first) +GLOBSORT="-mtime" +echo *.txt + +# Sort by size +GLOBSORT="size" +echo *.txt + +# Reverse alphabetical +GLOBSORT="reverse" +echo *.txt +``` + +**Options:** +- `name` - Alphabetical (default) +- `reverse` - Reverse alphabetical +- `size` - By file size +- `mtime` - By modification time +- `-mtime` - Reverse modification time + +### 7. BASH_TRAPSIG Variable + +**New: Signal number variable in traps**: + +```bash +#!/usr/bin/env bash +set -euo pipefail + +# BASH_TRAPSIG contains the signal number being handled +handle_signal() { + echo "Caught signal: $BASH_TRAPSIG" >&2 + case "$BASH_TRAPSIG" in + 15) echo "SIGTERM (15) received, shutting down gracefully" ;; + 2) echo "SIGINT (2) received, cleaning up" ;; + *) echo "Signal $BASH_TRAPSIG received" ;; + esac +} + +trap handle_signal SIGTERM SIGINT SIGHUP +``` + +**Benefits:** +- Reusable signal handlers +- Dynamic signal-specific behavior +- Better logging and debugging + +### 8. Floating-Point Arithmetic + +**New: `fltexpr` loadable builtin**: + +```bash +# Enable floating-point support +enable -f /usr/lib/bash/fltexpr fltexpr + +# Perform calculations +fltexpr result = 42.5 * 1.5 +echo "$result" # 63.75 + +# Complex expressions +fltexpr pi_area = 3.14159 * 5 * 5 +echo "Area: $pi_area" +``` + +**Use Cases:** +- Scientific calculations +- Financial computations +- Avoid external tools (bc, awk) + +## Performance Improvements + +### Avoid Subshells + +```bash +# ❌ OLD (Bash < 5.3) - Multiple subshells +for i in {1..1000}; do + result=$(echo "$i * 2" | bc) + process "$result" +done + +# ✅ NEW (Bash 5.3+) - No subshells +for i in {1..1000}; do + result=${ echo $((i * 2)); } + process "$result" +done +``` + +**Performance Gain:** ~40% faster in benchmarks + +### Efficient File Processing + +```bash +#!/usr/bin/env bash + +# Process large file efficiently +process_log() { + local line_count=0 + local error_count=0 + + while IFS= read -r line; do + ((line_count++)) + + # Bash 5.3: No subshell for grep + if ${ grep -q "ERROR" <<< "$line"; }; then + ((error_count++)) + fi + done < "$1" + + echo "Processed $line_count lines, found $error_count errors" +} + +process_log /var/log/app.log +``` + +## Migration Guide + +### Check Bash Version + +```bash +#!/usr/bin/env bash + +# Require Bash 5.3+ +if ((BASH_VERSINFO[0] < 5 || (BASH_VERSINFO[0] == 5 && BASH_VERSINFO[1] < 3))); then + echo "Error: Bash 5.3+ required (found $BASH_VERSION)" >&2 + exit 1 +fi +``` + +### Feature Detection + +```bash +# Test for 5.3 features +has_bash_53_features() { + # Try using ${ } syntax + if eval 'test=${ echo "yes"; }' 2>/dev/null; then + return 0 + else + return 1 + fi +} + +if has_bash_53_features; then + echo "Bash 5.3 features available" +else + echo "Using legacy mode" +fi +``` + +### Gradual Adoption + +```bash +#!/usr/bin/env bash +set -euo pipefail + +# Support both old and new bash +if ((BASH_VERSINFO[0] > 5 || (BASH_VERSINFO[0] == 5 && BASH_VERSINFO[1] >= 3))); then + # Bash 5.3+ path + result=${ compute_value; } +else + # Legacy path + result=$(compute_value) +fi +``` + +## Best Practices (2025) + +1. **Use ${ } for performance-critical loops** + ```bash + for item in "${large_array[@]}"; do + processed=${ transform "$item"; } + done + ``` + +2. **Use ${| } for clean temporary values** + ```bash + ${| calculate_hash "$file"; } + if [[ "$REPLY" == "$expected_hash" ]]; then + echo "Valid" + fi + ``` + +3. **Enable readline for interactive scripts** + ```bash + read -E -p "Config file: " config + ``` + +4. **Use source -p for modular libraries** + ```bash + source -p "$LIB_PATH" database.sh logging.sh + ``` + +5. **Document version requirements** + ```bash + # Requires: Bash 5.3+ for performance features + ``` + +## Compatibility Notes + +### Bash 5.3 Availability (2025) + +**Note:** Bash 5.3 (released July 2025) is the latest stable version. There is no Bash 5.4 as of October 2025. + +- **Linux**: Ubuntu 24.04+, Fedora 40+, Arch (current) +- **macOS**: Homebrew (`brew install bash`) +- **Windows**: WSL2 with Ubuntu 24.04+ +- **Containers**: `bash:5.3` official image + +### C23 Conformance + +Bash 5.3 updated to C23 language standard. **Note:** K&R style C compilers are no longer supported. + +### Fallback Pattern + +```bash +#!/usr/bin/env bash +set -euo pipefail + +# Detect bash version +readonly BASH_53_PLUS=$((BASH_VERSINFO[0] > 5 || (BASH_VERSINFO[0] == 5 && BASH_VERSINFO[1] >= 3))) + +process_items() { + local item + for item in "$@"; do + if ((BASH_53_PLUS)); then + result=${ transform "$item"; } # Fast path + else + result=$(transform "$item") # Compatible path + fi + echo "$result" + done +} +``` + +## Real-World Examples + +### Fast Log Parser + +```bash +#!/usr/bin/env bash +set -euo pipefail + +# Parse log file (Bash 5.3 optimized) +parse_log() { + local file="$1" + local stats_errors=0 + local stats_warnings=0 + local stats_lines=0 + + while IFS= read -r line; do + ((stats_lines++)) + + # Fast pattern matching (no subshell) + ${| grep -q "ERROR" <<< "$line"; } && ((stats_errors++)) + ${| grep -q "WARN" <<< "$line"; } && ((stats_warnings++)) + done < "$file" + + echo "Lines: $stats_lines, Errors: $stats_errors, Warnings: $stats_warnings" +} + +parse_log /var/log/application.log +``` + +### Interactive Configuration + +```bash +#!/usr/bin/env bash +set -euo pipefail + +# Interactive setup with readline +setup_config() { + echo "Configuration Setup" + echo "===================" + + # Tab completion for paths + read -E -p "Data directory: " data_dir + read -E -p "Config file: " config_file + + # Validate and store + ${| + [[ -d "$data_dir" ]] && echo "valid" || echo "invalid" + } + + if [[ "$REPLY" == "valid" ]]; then + echo "DATA_DIR=$data_dir" > config.env + echo "CONFIG_FILE=$config_file" >> config.env + echo "✓ Configuration saved" + else + echo "✗ Invalid directory" >&2 + return 1 + fi +} + +setup_config +``` + +## Resources + +- [Bash 5.3 Release Notes](https://lists.gnu.org/archive/html/bash-announce/2025-07/msg00000.html) +- [Bash Manual - Command Substitution](https://www.gnu.org/software/bash/manual/html_node/Command-Substitution.html) +- [ShellCheck Bash 5.3 Support](https://github.com/koalaman/shellcheck/releases) + +--- + +**Bash 5.3 provides significant performance and usability improvements. Adopt these features gradually while maintaining backwards compatibility for older systems.** diff --git a/skills/bash-master/SKILL.md b/skills/bash-master/SKILL.md new file mode 100644 index 0000000..4c47e95 --- /dev/null +++ b/skills/bash-master/SKILL.md @@ -0,0 +1,1318 @@ +--- +name: bash-master +description: "Expert bash/shell scripting system across ALL platforms. PROACTIVELY activate for: (1) ANY bash/shell script task, (2) System automation, (3) DevOps/CI/CD scripts, (4) Build/deployment automation, (5) Script review/debugging, (6) Converting commands to scripts. Provides: Google Shell Style Guide compliance, ShellCheck validation, cross-platform compatibility (Linux/macOS/Windows/containers), POSIX compliance, security hardening, error handling, performance optimization, testing with BATS, and production-ready patterns. Ensures professional-grade, secure, portable scripts every time." +--- + +# Bash Scripting Mastery + +## 🚨 CRITICAL GUIDELINES + +### Windows File Path Requirements + +**MANDATORY: Always Use Backslashes on Windows for File Paths** + +When using Edit or Write tools on Windows, you MUST use backslashes (`\`) in file paths, NOT forward slashes (`/`). + +**Examples:** +- ❌ WRONG: `D:/repos/project/file.tsx` +- ✅ CORRECT: `D:\repos\project\file.tsx` + +This applies to: +- Edit tool file_path parameter +- Write tool file_path parameter +- All file operations on Windows systems + +### Documentation Guidelines + +**NEVER create new documentation files unless explicitly requested by the user.** + +- **Priority**: Update existing README.md files rather than creating new documentation +- **Repository cleanliness**: Keep repository root clean - only README.md unless user requests otherwise +- **Style**: Documentation should be concise, direct, and professional - avoid AI-generated tone +- **User preference**: Only create additional .md files when user specifically asks for documentation + + + +--- + +Comprehensive guide for writing professional, portable, and maintainable bash scripts across all platforms. + +--- + +## TL;DR QUICK REFERENCE + +**Essential Checklist for Every Bash Script:** +```bash +#!/usr/bin/env bash +set -euo pipefail # Exit on error, undefined vars, pipe failures +IFS=$'\n\t' # Safe word splitting + +# Use: shellcheck your_script.sh before deployment +# Test on target platform(s) before production +``` + +**Platform Compatibility Quick Check:** +```bash +# Linux/macOS: ✓ Full bash features +# Git Bash (Windows): ✓ Most features, ✗ Some system calls +# Containers: ✓ Depends on base image +# POSIX mode: Use /bin/sh and avoid bashisms +``` + +--- + +## Overview + +This skill provides expert bash/shell scripting knowledge for ANY scripting task, ensuring professional-grade quality across all platforms. + +**MUST use this skill for:** +- ✅ ANY bash/shell script creation or modification +- ✅ System automation and tooling +- ✅ DevOps/CI/CD pipeline scripts +- ✅ Build and deployment automation +- ✅ Script review, debugging, or optimization +- ✅ Converting manual commands to automated scripts +- ✅ Cross-platform script compatibility + +**What this skill provides:** +- **Google Shell Style Guide compliance** - Industry-standard formatting and patterns +- **ShellCheck validation** - Automatic detection of common issues +- **Cross-platform compatibility** - Linux, macOS, Windows (Git Bash/WSL), containers +- **POSIX compliance** - Portable scripts that work everywhere +- **Security hardening** - Input validation, injection prevention, privilege management +- **Error handling** - Robust `set -euo pipefail`, trap handlers, exit codes +- **Performance optimization** - Efficient patterns, avoiding anti-patterns +- **Testing with BATS** - Unit testing, integration testing, CI/CD integration +- **Debugging techniques** - Logging, troubleshooting, profiling +- **Production-ready patterns** - Templates and best practices for real-world use + +**This skill activates automatically for:** +- Any mention of "bash", "shell", "script" in task +- System automation requests +- DevOps/CI/CD tasks +- Build/deployment automation +- Command line tool creation + +--- + +## Core Principles + +### 1. Safety First + +**ALWAYS start scripts with safety settings:** + +```bash +#!/usr/bin/env bash + +# Fail fast and loud +set -e # Exit on any error +set -u # Exit on undefined variable +set -o pipefail # Exit on pipe failure +set -E # ERR trap inherited by functions + +# Optionally: +# set -x # Debug mode (print commands before execution) +# set -C # Prevent file overwrites with redirection + +# Safe word splitting +IFS=$'\n\t' + +# Script metadata +readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +readonly SCRIPT_NAME="$(basename "${BASH_SOURCE[0]}")" +``` + +**Why this matters:** +- `set -e`: Prevents cascading failures +- `set -u`: Catches typos in variable names +- `set -o pipefail`: Catches failures in the middle of pipes +- `IFS=$'\n\t'`: Prevents word splitting on spaces (security issue) + +### 2. POSIX Compatibility vs Bash Features + +**Know when to use which:** + +```bash +# POSIX-compliant (portable across shells) +#!/bin/sh +# Use: [ ] tests, no arrays, no [[ ]], no <(process substitution) + +# Bash-specific (modern features, clearer syntax) +#!/usr/bin/env bash +# Use: [[ ]], arrays, associative arrays, <(), process substitution +``` + +**Decision matrix:** +- Need to run on any UNIX system → Use `#!/bin/sh` and POSIX only +- Control the environment (modern Linux/macOS) → Use `#!/usr/bin/env bash` +- Need advanced features (arrays, regex) → Use `#!/usr/bin/env bash` + +### 3. Quoting Rules (Critical) + +```bash +# ALWAYS quote variables to prevent word splitting and globbing +bad_cmd=$file_path # ✗ WRONG - word splitting +good_cmd="$file_path" # ✓ CORRECT + +# Arrays: Quote expansion +files=("file 1.txt" "file 2.txt") +process "${files[@]}" # ✓ CORRECT - each element quoted +process "${files[*]}" # ✗ WRONG - all elements as one string + +# Command substitution: Quote the result +result="$(command)" # ✓ CORRECT +result=$(command) # ✗ WRONG (unless you want word splitting) + +# Exception: When you WANT word splitting +# shellcheck disable=SC2086 +flags="-v -x -z" +command $flags # Intentional word splitting +``` + +### 4. Use ShellCheck + +**ALWAYS run ShellCheck before deployment:** + +```bash +# Install +# Ubuntu/Debian: apt-get install shellcheck +# macOS: brew install shellcheck +# Windows: scoop install shellcheck + +# Usage +shellcheck your_script.sh +shellcheck -x your_script.sh # Follow source statements + +# In CI/CD +find . -name "*.sh" -exec shellcheck {} + +``` + +**ShellCheck catches:** +- Quoting issues +- Bashisms in POSIX scripts +- Common logic errors +- Security vulnerabilities +- Performance anti-patterns + +--- + +## Platform-Specific Considerations + + +### Windows (Git Bash) Path Conversion - CRITICAL + +**ESSENTIAL KNOWLEDGE:** Git Bash/MINGW automatically converts Unix-style paths to Windows paths. This is the most common source of cross-platform scripting errors on Windows. + +**Complete Guide:** See `references/windows-git-bash-paths.md` for comprehensive documentation. + +**Quick Reference:** + +```bash +# Automatic conversion happens for: +/foo → C:/Program Files/Git/usr/foo +--dir=/tmp → --dir=C:/msys64/tmp + +# Disable conversion when needed +MSYS_NO_PATHCONV=1 command /path/that/should/not/convert + +# Manual conversion with cygpath +unix_path=$(cygpath -u "C:\Windows\System32") # Windows to Unix +win_path=$(cygpath -w "/c/Users/username") # Unix to Windows + +# Shell detection (fastest method) +if [[ "$OSTYPE" == "msys" ]] || [[ "$OSTYPE" == "mingw"* ]]; then + echo "Git Bash detected" + # Use path conversion +fi + +# Or check $MSYSTEM variable (Git Bash/MSYS2 specific) +case "${MSYSTEM:-}" in + MINGW64|MINGW32|MSYS) + echo "MSYS2/Git Bash environment: $MSYSTEM" + ;; +esac +``` + +**Common Issues:** + +```bash +# Problem: Flags converted to paths +command /e /s # /e becomes C:/Program Files/Git/e + +# Solution: Use double slashes or dashes +command //e //s # OR: command -e -s + +# Problem: Spaces in paths +cd C:\Program Files\Git # Fails + +# Solution: Quote paths +cd "C:\Program Files\Git" # OR: cd /c/Program\ Files/Git +``` + + +### Linux + +**Primary target for most bash scripts:** + +```bash +# Linux-specific features available +/proc filesystem +systemd integration +Linux-specific commands (apt, yum, systemctl) + +# Check for Linux +if [[ "$OSTYPE" == "linux-gnu"* ]]; then + # Linux-specific code +fi +``` + +### macOS + +**BSD-based utilities (different from GNU):** + +```bash +# macOS differences +sed -i '' # macOS requires empty string +sed -i # Linux doesn't need it + +# Use ggrep, gsed, etc. for GNU versions +if command -v gsed &> /dev/null; then + SED=gsed +else + SED=sed +fi + +# Check for macOS +if [[ "$OSTYPE" == "darwin"* ]]; then + # macOS-specific code +fi +``` + +### Windows (Git Bash / WSL) + +**Git Bash limitations:** + +```bash +# Available in Git Bash: +- Most core utils +- File operations +- Process management (limited) + +# NOT available: +- systemd +- Some signals (SIGHUP behavior differs) +- /proc filesystem +- Native Windows path handling issues + +# Path handling +# Git Bash uses Unix paths: /c/Users/... +# Convert if needed: +winpath=$(cygpath -w "$unixpath") # Unix → Windows +unixpath=$(cygpath -u "$winpath") # Windows → Unix + +# Check for Git Bash +if [[ "$OSTYPE" == "msys" ]] || [[ "$OSTYPE" == "cygwin" ]]; then + # Git Bash / Cygwin code +fi +``` + +**WSL (Windows Subsystem for Linux):** +```bash +# WSL is essentially Linux, but: +# - Can access Windows filesystem at /mnt/c/ +# - Some syscalls behave differently +# - Network configuration differs + +# Check for WSL +if grep -qi microsoft /proc/version 2>/dev/null; then + # WSL-specific code +fi +``` + +### Containers (Docker/Kubernetes) + +**Container-aware scripting:** + +```bash +# Minimal base images may not have bash +# Use #!/bin/sh or install bash explicitly + +# Container detection +if [ -f /.dockerenv ] || grep -q docker /proc/1/cgroup 2>/dev/null; then + # Running in Docker +fi + +# Kubernetes detection +if [ -n "$KUBERNETES_SERVICE_HOST" ]; then + # Running in Kubernetes +fi + +# Best practices: +# - Minimize dependencies +# - Use absolute paths or PATH +# - Don't assume user/group existence +# - Handle signals properly (PID 1 issues) +``` + +### Cross-Platform Template + +```bash +#!/usr/bin/env bash +set -euo pipefail + +# Detect platform +detect_platform() { + case "$OSTYPE" in + linux-gnu*) echo "linux" ;; + darwin*) echo "macos" ;; + msys*|cygwin*) echo "windows" ;; + *) echo "unknown" ;; + esac +} + +PLATFORM=$(detect_platform) + +# Platform-specific paths +case "$PLATFORM" in + linux) + SED=sed + ;; + macos) + SED=$(command -v gsed || echo sed) + ;; + windows) + # Git Bash specifics + ;; +esac +``` + +--- + +## Best Practices + +### Function Design + +```bash +# Good function structure +function_name() { + # 1. Local variables first + local arg1="$1" + local arg2="${2:-default_value}" + local result="" + + # 2. Input validation + if [[ -z "$arg1" ]]; then + echo "Error: arg1 is required" >&2 + return 1 + fi + + # 3. Main logic + result=$(some_operation "$arg1" "$arg2") + + # 4. Output/return + echo "$result" + return 0 +} + +# Use functions, not scripts-in-scripts +# Benefits: testability, reusability, namespacing +``` + +### Variable Naming + +```bash +# Constants: UPPER_CASE +readonly MAX_RETRIES=3 +readonly CONFIG_FILE="/etc/app/config.conf" + +# Global variables: UPPER_CASE or lower_case (be consistent) +GLOBAL_STATE="initialized" + +# Local variables: lower_case +local user_name="john" +local file_count=0 + +# Environment variables: UPPER_CASE (by convention) +export DATABASE_URL="postgres://..." + +# Readonly when possible +readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +``` + +### Error Handling + +```bash +# Method 1: Check exit codes explicitly +if ! command_that_might_fail; then + echo "Error: Command failed" >&2 + return 1 +fi + +# Method 2: Use || for alternative actions +command_that_might_fail || { + echo "Error: Command failed" >&2 + return 1 +} + +# Method 3: Trap for cleanup +cleanup() { + local exit_code=$? + # Cleanup operations + rm -f "$TEMP_FILE" + exit "$exit_code" +} +trap cleanup EXIT + +# Method 4: Custom error handler +error_exit() { + local message="$1" + local code="${2:-1}" + echo "Error: $message" >&2 + exit "$code" +} + +# Usage +[[ -f "$config_file" ]] || error_exit "Config file not found: $config_file" +``` + +### Input Validation + +```bash +validate_input() { + local input="$1" + + # Check if empty + if [[ -z "$input" ]]; then + echo "Error: Input cannot be empty" >&2 + return 1 + fi + + # Check format (example: alphanumeric only) + if [[ ! "$input" =~ ^[a-zA-Z0-9_-]+$ ]]; then + echo "Error: Input contains invalid characters" >&2 + return 1 + fi + + # Check length + if [[ ${#input} -gt 255 ]]; then + echo "Error: Input too long (max 255 characters)" >&2 + return 1 + fi + + return 0 +} + +# Validate before use +read -r user_input +if validate_input "$user_input"; then + process "$user_input" +fi +``` + +### Argument Parsing + +```bash +# Simple argument parsing +usage() { + cat < + +Options: + -h, --help Show this help + -v, --verbose Verbose output + -f, --file FILE Input file + -o, --output DIR Output directory + +Commands: + build Build the project + test Run tests +EOF +} + +main() { + local verbose=false + local input_file="" + local output_dir="." + local command="" + + # Parse arguments + while [[ $# -gt 0 ]]; do + case "$1" in + -h|--help) + usage + exit 0 + ;; + -v|--verbose) + verbose=true + shift + ;; + -f|--file) + input_file="$2" + shift 2 + ;; + -o|--output) + output_dir="$2" + shift 2 + ;; + -*) + echo "Error: Unknown option: $1" >&2 + usage >&2 + exit 1 + ;; + *) + command="$1" + shift + break + ;; + esac + done + + # Validate required arguments + if [[ -z "$command" ]]; then + echo "Error: Command is required" >&2 + usage >&2 + exit 1 + fi + + # Execute command + case "$command" in + build) do_build ;; + test) do_test ;; + *) + echo "Error: Unknown command: $command" >&2 + usage >&2 + exit 1 + ;; + esac +} + +main "$@" +``` + +### Logging + +```bash +# Logging levels +readonly LOG_LEVEL_DEBUG=0 +readonly LOG_LEVEL_INFO=1 +readonly LOG_LEVEL_WARN=2 +readonly LOG_LEVEL_ERROR=3 + +# Current log level +LOG_LEVEL=${LOG_LEVEL:-$LOG_LEVEL_INFO} + +log_debug() { [[ $LOG_LEVEL -le $LOG_LEVEL_DEBUG ]] && echo "[DEBUG] $*" >&2; } +log_info() { [[ $LOG_LEVEL -le $LOG_LEVEL_INFO ]] && echo "[INFO] $*" >&2; } +log_warn() { [[ $LOG_LEVEL -le $LOG_LEVEL_WARN ]] && echo "[WARN] $*" >&2; } +log_error() { [[ $LOG_LEVEL -le $LOG_LEVEL_ERROR ]] && echo "[ERROR] $*" >&2; } + +# With timestamps +log_with_timestamp() { + local level="$1" + shift + echo "[$(date +'%Y-%m-%d %H:%M:%S')] [$level] $*" >&2 +} + +# Usage +log_info "Starting process" +log_error "Failed to connect to database" +``` + +--- + +## Security Best Practices + +### Command Injection Prevention + +```bash +# NEVER use eval with user input +# ✗ WRONG - DANGEROUS +eval "$user_input" + +# NEVER use dynamic variable names from user input +# ✗ WRONG - DANGEROUS +eval "var_$user_input=value" + +# NEVER concatenate user input into commands +# ✗ WRONG - DANGEROUS +grep "$user_pattern" file.txt # If pattern contains -e flag, injection possible + +# ✓ CORRECT - Use arrays +grep_args=("$user_pattern" "file.txt") +grep "${grep_args[@]}" + +# ✓ CORRECT - Use -- to separate options from arguments +grep -- "$user_pattern" file.txt +``` + +### Path Traversal Prevention + +```bash +# Sanitize file paths +sanitize_path() { + local path="$1" + + # Remove .. components + path="${path//..\/}" + path="${path//\/..\//}" + + # Remove leading / + path="${path#/}" + + echo "$path" +} + +# Validate path is within allowed directory +is_safe_path() { + local file_path="$1" + local base_dir="$2" + + # Resolve to absolute path + local real_path + real_path=$(readlink -f "$file_path" 2>/dev/null) || return 1 + local real_base + real_base=$(readlink -f "$base_dir" 2>/dev/null) || return 1 + + # Check if path starts with base directory + [[ "$real_path" == "$real_base"/* ]] +} + +# Usage +if is_safe_path "$user_file" "/var/app/data"; then + process_file "$user_file" +else + echo "Error: Invalid file path" >&2 + exit 1 +fi +``` + +### Privilege Management + +```bash +# Check if running as root +if [[ $EUID -eq 0 ]]; then + echo "Error: Do not run this script as root" >&2 + exit 1 +fi + +# Drop privileges if needed +drop_privileges() { + local user="$1" + + if [[ $EUID -eq 0 ]]; then + exec sudo -u "$user" "$0" "$@" + fi +} + +# Run specific command with elevated privileges +run_as_root() { + if [[ $EUID -ne 0 ]]; then + sudo "$@" + else + "$@" + fi +} +``` + +### Temporary File Handling + +```bash +# Create secure temporary files +readonly TEMP_DIR=$(mktemp -d) +readonly TEMP_FILE=$(mktemp) + +# Cleanup on exit +cleanup() { + rm -rf "$TEMP_DIR" + rm -f "$TEMP_FILE" +} +trap cleanup EXIT + +# Secure temporary file (only readable by owner) +secure_temp=$(mktemp) +chmod 600 "$secure_temp" +``` + +--- + +## Performance Optimization + +### Avoid Unnecessary Subshells + +```bash +# ✗ SLOW - Creates subshell for each iteration +while IFS= read -r line; do + count=$(echo "$count + 1" | bc) +done < file.txt + +# ✓ FAST - Arithmetic in bash +count=0 +while IFS= read -r line; do + ((count++)) +done < file.txt +``` + +### Use Bash Built-ins + +```bash +# ✗ SLOW - External commands +dirname=$(dirname "$path") +basename=$(basename "$path") + +# ✓ FAST - Parameter expansion +dirname="${path%/*}" +basename="${path##*/}" + +# ✗ SLOW - grep for simple checks +if echo "$string" | grep -q "pattern"; then + +# ✓ FAST - Bash regex +if [[ "$string" =~ pattern ]]; then + +# ✗ SLOW - awk for simple extraction +field=$(echo "$line" | awk '{print $3}') + +# ✓ FAST - Read into array +read -ra fields <<< "$line" +field="${fields[2]}" +``` + +### Process Substitution vs Pipes + +```bash +# When you need to read multiple commands' output +# ✓ GOOD - Process substitution +while IFS= read -r line1 <&3 && IFS= read -r line2 <&4; do + echo "$line1 - $line2" +done 3< <(command1) 4< <(command2) + +# Parallel processing +command1 & +command2 & +wait # Wait for all background jobs +``` + +### Array Operations + +```bash +# ✓ FAST - Native array operations +files=(*.txt) +echo "Found ${#files[@]} files" + +# ✗ SLOW - Parsing ls output +count=$(ls -1 *.txt | wc -l) + +# ✓ FAST - Array filtering +filtered=() +for item in "${array[@]}"; do + [[ "$item" =~ ^[0-9]+$ ]] && filtered+=("$item") +done + +# ✓ FAST - Array joining +IFS=, +joined="${array[*]}" +IFS=$'\n\t' +``` + +--- + +## Testing + +### Unit Testing with BATS + +```bash +# Install BATS +# git clone https://github.com/bats-core/bats-core.git +# cd bats-core && ./install.sh /usr/local + +# test/script.bats +#!/usr/bin/env bats + +# Load script to test +load '../script.sh' + +@test "function returns correct value" { + result=$(my_function "input") + [ "$result" = "expected" ] +} + +@test "function handles empty input" { + run my_function "" + [ "$status" -eq 1 ] + [ "${lines[0]}" = "Error: Input cannot be empty" ] +} + +@test "function validates input format" { + run my_function "invalid@input" + [ "$status" -eq 1 ] +} + +# Run tests +# bats test/script.bats +``` + +### Integration Testing + +```bash +# integration_test.sh +#!/usr/bin/env bash +set -euo pipefail + +# Setup +setup() { + export TEST_DIR=$(mktemp -d) + export TEST_FILE="$TEST_DIR/test.txt" +} + +# Teardown +teardown() { + rm -rf "$TEST_DIR" +} + +# Test case +test_file_creation() { + ./script.sh create "$TEST_FILE" + + if [[ ! -f "$TEST_FILE" ]]; then + echo "FAIL: File was not created" + return 1 + fi + + echo "PASS: File creation works" + return 0 +} + +# Run tests +main() { + setup + trap teardown EXIT + + test_file_creation || exit 1 + + echo "All tests passed" +} + +main +``` + +### CI/CD Integration + +```yaml +# .github/workflows/test.yml +name: Test + +on: [push, pull_request] + +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: Install shellcheck + run: sudo apt-get install -y shellcheck + + - name: Run shellcheck + run: find . -name "*.sh" -exec shellcheck {} + + + - name: Install bats + run: | + git clone https://github.com/bats-core/bats-core.git + cd bats-core + sudo ./install.sh /usr/local + + - name: Run tests + run: bats test/ +``` + +--- + +## Debugging Techniques + +### Debug Mode + +```bash +# Method 1: set -x (print commands) +set -x +command1 +command2 +set +x # Turn off + +# Method 2: PS4 for better output +export PS4='+(${BASH_SOURCE}:${LINENO}): ${FUNCNAME[0]:+${FUNCNAME[0]}(): }' +set -x + +# Method 3: Conditional debugging +DEBUG=${DEBUG:-false} +debug() { + if [[ "$DEBUG" == "true" ]]; then + echo "[DEBUG] $*" >&2 + fi +} + +# Usage: DEBUG=true ./script.sh +``` + +### Tracing and Profiling + +```bash +# Trace function calls +trace() { + echo "[TRACE] Function: ${FUNCNAME[1]}, Args: $*" >&2 +} + +my_function() { + trace "$@" + # Function logic +} + +# Execution time profiling +profile() { + local start=$(date +%s%N) + "$@" + local end=$(date +%s%N) + local duration=$(( (end - start) / 1000000 )) + echo "[PROFILE] Command '$*' took ${duration}ms" >&2 +} + +# Usage +profile slow_command arg1 arg2 +``` + +### Common Issues and Solutions + +```bash +# Issue: Script works in bash but not in sh +# Solution: Check for bashisms +checkbashisms script.sh + +# Issue: Works locally but not on server +# Solution: Check PATH and environment +env +echo "$PATH" + +# Issue: Whitespace in filenames breaking script +# Solution: Always quote variables +for file in *.txt; do + process "$file" # Not: process $file +done + +# Issue: Script behaves differently in cron +# Solution: Set PATH explicitly +PATH=/usr/local/bin:/usr/bin:/bin +export PATH +``` + +--- + +## Advanced Patterns + +### Configuration File Parsing + +```bash +# Simple key=value config +load_config() { + local config_file="$1" + + if [[ ! -f "$config_file" ]]; then + echo "Error: Config file not found: $config_file" >&2 + return 1 + fi + + # Source config (dangerous if not trusted) + # shellcheck source=/dev/null + source "$config_file" +} + +# Safe config parsing (no code execution) +read_config() { + local config_file="$1" + + while IFS='=' read -r key value; do + # Skip comments and empty lines + [[ "$key" =~ ^[[:space:]]*# ]] && continue + [[ -z "$key" ]] && continue + + # Trim whitespace + key=$(echo "$key" | tr -d ' ') + value=$(echo "$value" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//') + + # Export variable + declare -g "$key=$value" + done < "$config_file" +} +``` + +### Parallel Processing + +```bash +# Simple background jobs +process_files_parallel() { + local max_jobs=4 + local job_count=0 + + for file in *.txt; do + # Start background job + process_file "$file" & + + # Limit concurrent jobs + ((job_count++)) + if [[ $job_count -ge $max_jobs ]]; then + wait -n # Wait for any job to finish + ((job_count--)) + fi + done + + # Wait for remaining jobs + wait +} + +# GNU Parallel (if available) +parallel_with_gnu() { + parallel -j 4 process_file ::: *.txt +} +``` + +### Signal Handling + +```bash +# Graceful shutdown +shutdown_requested=false + +handle_sigterm() { + echo "Received SIGTERM, shutting down gracefully..." >&2 + shutdown_requested=true +} + +trap handle_sigterm SIGTERM SIGINT + +main_loop() { + while [[ "$shutdown_requested" == "false" ]]; do + # Do work + sleep 1 + done + + echo "Shutdown complete" >&2 +} + +main_loop +``` + +### Retries with Exponential Backoff + +```bash +retry_with_backoff() { + local max_attempts=5 + local timeout=1 + local attempt=1 + local exitCode=0 + + while [[ $attempt -le $max_attempts ]]; do + if "$@"; then + return 0 + else + exitCode=$? + fi + + echo "Attempt $attempt failed! Retrying in $timeout seconds..." >&2 + sleep "$timeout" + attempt=$((attempt + 1)) + timeout=$((timeout * 2)) + done + + echo "Command failed after $max_attempts attempts!" >&2 + return "$exitCode" +} + +# Usage +retry_with_backoff curl -f https://api.example.com/health +``` + +--- + +## Resources for Additional Information + +### Official Documentation + +1. **Bash Reference Manual** + - URL: https://www.gnu.org/software/bash/manual/ + - The authoritative source for bash features and behavior + +2. **POSIX Shell Command Language** + - URL: https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html + - For writing portable scripts + +### Style Guides + +1. **Google Shell Style Guide** + - URL: https://google.github.io/styleguide/shellguide.html + - Industry-standard practices from Google + +2. **Defensive Bash Programming** + - URL: https://kfirlavi.herokuapp.com/blog/2012/11/14/defensive-bash-programming + - Best practices for robust scripts + +### Tools + +1. **ShellCheck** + - URL: https://www.shellcheck.net/ + - GitHub: https://github.com/koalaman/shellcheck + - Static analysis tool for shell scripts + +2. **BATS (Bash Automated Testing System)** + - GitHub: https://github.com/bats-core/bats-core + - Unit testing framework for bash + +3. **shfmt** + - GitHub: https://github.com/mvdan/sh + - Shell script formatter + +### Learning Resources + +1. **Bash Academy** + - URL: https://www.bash.academy/ + - Comprehensive bash learning resource + +2. **Bash Guide for Beginners** + - URL: https://tldp.org/LDP/Bash-Beginners-Guide/html/ + - From The Linux Documentation Project + +3. **Advanced Bash-Scripting Guide** + - URL: https://tldp.org/LDP/abs/html/ + - In-depth coverage of advanced topics + +4. **Bash Pitfalls** + - URL: https://mywiki.wooledge.org/BashPitfalls + - Common mistakes and how to avoid them + +5. **explainshell.com** + - URL: https://explainshell.com/ + - Interactive tool to explain shell commands + +### Platform-Specific Resources + +1. **GNU Coreutils Manual** + - URL: https://www.gnu.org/software/coreutils/manual/ + - For Linux-specific commands + +2. **FreeBSD Manual Pages** + - URL: https://www.freebsd.org/cgi/man.cgi + - For macOS (BSD-based) differences + +3. **Git for Windows** + - URL: https://gitforwindows.org/ + - Git Bash documentation and issues + +4. **WSL Documentation** + - URL: https://docs.microsoft.com/en-us/windows/wsl/ + - Windows Subsystem for Linux specifics + +### Community Resources + +1. **Stack Overflow - Bash Tag** + - URL: https://stackoverflow.com/questions/tagged/bash + - Community Q&A + +2. **Unix & Linux Stack Exchange** + - URL: https://unix.stackexchange.com/ + - Shell scripting expertise + +3. **Reddit - r/bash** + - URL: https://www.reddit.com/r/bash/ + - Community discussions + +### Quick Reference + +1. **Bash Cheat Sheet** + - URL: https://devhints.io/bash + - Quick syntax reference + +2. **ShellCheck Wiki** + - URL: https://www.shellcheck.net/wiki/ + - Explanations of ShellCheck warnings + +--- + +## Reference Files + +For deeper coverage of specific topics, see the reference files: + +- **[references/platform_specifics.md](references/platform_specifics.md)** - Detailed platform differences and workarounds +- **[references/best_practices.md](references/best_practices.md)** - Comprehensive industry standards and guidelines +- **[references/patterns_antipatterns.md](references/patterns_antipatterns.md)** - Common patterns and pitfalls with solutions + +--- + +## When to Use This Skill + +**Always activate for:** +- Writing new bash scripts +- Reviewing/refactoring existing scripts +- Debugging shell script issues +- Cross-platform shell scripting +- DevOps automation tasks +- CI/CD pipeline scripts +- System administration automation + +**Key indicators:** +- User mentions bash, shell, or script +- Task involves automation +- Platform compatibility is a concern +- Security or robustness is important +- Performance optimization needed + +--- + +## Success Criteria + +A bash script using this skill should: + +1. ✓ Pass ShellCheck with no warnings +2. ✓ Include proper error handling (set -euo pipefail) +3. ✓ Quote all variable expansions +4. ✓ Include usage/help text +5. ✓ Use functions for reusable logic +6. ✓ Include appropriate comments +7. ✓ Handle edge cases (empty input, missing files, etc.) +8. ✓ Work across target platforms +9. ✓ Follow consistent style (Google Shell Style Guide) +10. ✓ Include cleanup (trap EXIT) + +**Quality checklist:** +```bash +# Run before deployment +shellcheck script.sh # No errors or warnings +bash -n script.sh # Syntax check +bats test/script.bats # Unit tests pass +./script.sh --help # Usage text displays +DEBUG=true ./script.sh # Debug mode works +``` + +--- + +## Troubleshooting + +### Script fails on different platform +1. Check for bashisms: `checkbashisms script.sh` +2. Verify commands exist: `command -v tool_name` +3. Test command flags: `sed --version` (GNU) vs `sed` (BSD) + +### ShellCheck warnings +1. Read the explanation: `shellcheck -W SC2086` +2. Fix the issue (don't just disable) +3. Only disable with justification: `# shellcheck disable=SC2086 reason: intentional word splitting` + +### Script works interactively but fails in cron +1. Set PATH explicitly +2. Use absolute paths +3. Redirect output for debugging: `./script.sh >> /tmp/cron.log 2>&1` + +### Performance issues +1. Profile with `time command` +2. Enable tracing: `set -x` +3. Avoid unnecessary subshells and external commands +4. Use bash built-ins where possible + +--- + +This skill provides comprehensive bash scripting knowledge. Combined with the reference files, you have access to industry-standard practices and platform-specific guidance for any bash scripting task. diff --git a/skills/bash-master/references/best_practices.md b/skills/bash-master/references/best_practices.md new file mode 100644 index 0000000..cce47c7 --- /dev/null +++ b/skills/bash-master/references/best_practices.md @@ -0,0 +1,1364 @@ +# Bash Scripting Best Practices & Industry Standards + +Comprehensive guide to professional bash scripting following industry standards including Google Shell Style Guide, ShellCheck recommendations, and community best practices. + +--- + +## Table of Contents + +1. [Script Structure](#script-structure) +2. [Safety and Robustness](#safety-and-robustness) +3. [Style Guidelines](#style-guidelines) +4. [Functions](#functions) +5. [Variables](#variables) +6. [Error Handling](#error-handling) +7. [Input/Output](#inputoutput) +8. [Security](#security) +9. [Performance](#performance) +10. [Documentation](#documentation) +11. [Testing](#testing) +12. [Maintenance](#maintenance) + +--- + +## 🚨 CRITICAL GUIDELINES + +### Windows File Path Requirements + +**MANDATORY: Always Use Backslashes on Windows for File Paths** + +When using Edit or Write tools on Windows, you MUST use backslashes (`\`) in file paths, NOT forward slashes (`/`). + +**Examples:** +- ❌ WRONG: `D:/repos/project/file.tsx` +- ✅ CORRECT: `D:\repos\project\file.tsx` + +This applies to: +- Edit tool file_path parameter +- Write tool file_path parameter +- All file operations on Windows systems + + +### Documentation Guidelines + +**NEVER create new documentation files unless explicitly requested by the user.** + +- **Priority**: Update existing README.md files rather than creating new documentation +- **Repository cleanliness**: Keep repository root clean - only README.md unless user requests otherwise +- **Style**: Documentation should be concise, direct, and professional - avoid AI-generated tone +- **User preference**: Only create additional .md files when user specifically asks for documentation + + +--- + +## Script Structure + +### Standard Template + +```bash +#!/usr/bin/env bash +# +# Script Name: script_name.sh +# Description: Brief description of what this script does +# Author: Your Name +# Date: 2024-01-01 +# Version: 1.0.0 +# +# Usage: script_name.sh [OPTIONS] +# +# Options: +# -h, --help Show help message +# -v, --verbose Enable verbose output +# +# Dependencies: +# - bash >= 4.0 +# - jq +# - curl +# +# Exit Codes: +# 0 - Success +# 1 - General error +# 2 - Invalid arguments +# 3 - Missing dependency +# + +set -euo pipefail +IFS=$'\n\t' + +# Script metadata +readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +readonly SCRIPT_NAME="$(basename "${BASH_SOURCE[0]}")" +readonly SCRIPT_VERSION="1.0.0" + +# Global constants +readonly DEFAULT_TIMEOUT=30 +readonly CONFIG_FILE="${CONFIG_FILE:-$SCRIPT_DIR/config.conf}" + +# Global variables +VERBOSE=false +DRY_RUN=false + +#------------------------------------------------------------------------------ +# Functions +#------------------------------------------------------------------------------ + +# Show usage information +usage() { + cat < + +Description of what the script does. + +OPTIONS: + -h, --help Show this help message + -v, --verbose Enable verbose output + -n, --dry-run Show what would be done without doing it + -V, --version Show version + +COMMANDS: + build Build the project + test Run tests + deploy Deploy to production + +EXAMPLES: + $SCRIPT_NAME build + $SCRIPT_NAME --verbose test + $SCRIPT_NAME deploy --dry-run + +EOF +} + +# Cleanup function +cleanup() { + local exit_code=$? + # Remove temporary files + [[ -n "${TEMP_DIR:-}" ]] && rm -rf "$TEMP_DIR" + exit "$exit_code" +} + +# Main function +main() { + # Parse arguments + parse_arguments "$@" + + # Validate dependencies + check_dependencies + + # Main script logic here + echo "Script execution complete" +} + +#------------------------------------------------------------------------------ +# Script execution +#------------------------------------------------------------------------------ + +# Set up cleanup trap +trap cleanup EXIT INT TERM + +# Run main function with all arguments +main "$@" +``` + +### File Organization + +```bash +# For larger projects, organize code into modules + +# project/ +# ├── bin/ +# │ └── main.sh # Entry point +# ├── lib/ +# │ ├── common.sh # Shared utilities +# │ ├── config.sh # Configuration handling +# │ └── logger.sh # Logging functions +# ├── config/ +# │ └── default.conf # Default configuration +# ├── test/ +# │ ├── test_common.bats # Unit tests +# │ └── test_config.bats +# └── README.md + +# In main.sh: +# Source library files +readonly LIB_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../lib" && pwd)" + +# shellcheck source=lib/common.sh +source "$LIB_DIR/common.sh" +# shellcheck source=lib/logger.sh +source "$LIB_DIR/logger.sh" +``` + +--- + +## Safety and Robustness + +### Essential Safety Settings + +```bash +# ALWAYS use these at the start of scripts +set -e # Exit immediately if a command exits with a non-zero status +set -u # Treat unset variables as an error +set -o pipefail # Return value of a pipeline is status of last command to exit with non-zero status +set -E # ERR trap is inherited by shell functions + +# Optionally add: +set -x # Print commands before executing (debugging) +set -C # Prevent output redirection from overwriting existing files +``` + +### Safe Word Splitting + +```bash +# Default IFS causes issues with filenames containing spaces +# OLD IFS: space, tab, newline +IFS=$' \t\n' + +# SAFE IFS: only tab and newline +IFS=$'\n\t' + +# This prevents word splitting on spaces, which is a common source of bugs: +files="file1.txt file2.txt" +for file in $files; do # Without proper IFS, this splits on spaces! + echo "$file" +done +``` + +### Quoting Rules + +```bash +# ALWAYS quote variable expansions +command "$variable" # ✓ CORRECT +command $variable # ✗ WRONG (word splitting and globbing) + +# Arrays: Proper expansion +files=("file1.txt" "file 2.txt" "file 3.txt") +process "${files[@]}" # ✓ CORRECT (each element separate) +process "${files[*]}" # ✗ WRONG (all elements as one string) +process ${files[@]} # ✗ WRONG (unquoted, word splitting) + +# Command substitution: Quote the result +result="$(command)" # ✓ CORRECT +result=$(command) # ✗ WRONG (unless word splitting is desired) + +# Glob patterns: Don't quote when you want globbing +for file in *.txt; do # ✓ CORRECT (globbing intended) + echo "$file" # ✓ CORRECT (no globbing inside) +done + +for file in "*.txt"; do # ✗ WRONG (literal "*.txt", no globbing) + echo "$file" +done +``` + +### Handling Special Characters + +```bash +# Filenames with special characters +# Use quotes and proper escaping + +# Create array from find output +mapfile -t files < <(find . -name "*.txt" -print0 | xargs -0) + +# Or modern bash: +files=() +while IFS= read -r -d '' file; do + files+=("$file") +done < <(find . -name "*.txt" -print0) + +# Process files safely +for file in "${files[@]}"; do + [[ -f "$file" ]] && process "$file" +done +``` + +--- + +## Style Guidelines + +Based on Google Shell Style Guide and community standards. + +### Naming Conventions + +```bash +# Constants: UPPER_CASE with underscores +readonly MAX_RETRIES=3 +readonly DEFAULT_TIMEOUT=30 +readonly CONFIG_DIR="/etc/myapp" + +# Environment variables: UPPER_CASE (by convention) +export DATABASE_URL="postgres://localhost/db" +export LOG_LEVEL="INFO" + +# Global variables: UPPER_CASE or lower_case (be consistent in your project) +GLOBAL_COUNTER=0 +current_state="initialized" + +# Local variables: lower_case with underscores +local user_name="john" +local file_count=0 +local error_message="" + +# Functions: lower_case with underscores +function_name() { + local var="value" +} + +# Private functions: Prefix with underscore +_internal_function() { + # Helper function not meant to be called externally +} +``` + +### Indentation and Formatting + +```bash +# Use 4 spaces for indentation (not tabs) +# Or 2 spaces (be consistent) + +# Function definition +my_function() { + local arg="$1" + + if [[ -n "$arg" ]]; then + echo "Processing $arg" + else + echo "No argument provided" + return 1 + fi + + return 0 +} + +# Conditional blocks +if [[ condition ]]; then + # code +elif [[ other_condition ]]; then + # code +else + # code +fi + +# Loops +for item in "${array[@]}"; do + # code +done + +while [[ condition ]]; do + # code +done + +# Case statement +case "$variable" in + pattern1) + # code + ;; + pattern2) + # code + ;; + *) + # default + ;; +esac + +# Line length: Prefer < 80 characters, max 100 +# Break long lines with backslash +long_command \ + --option1 value1 \ + --option2 value2 \ + --option3 value3 + +# Or use arrays for readability +command_args=( + --option1 value1 + --option2 value2 + --option3 value3 +) +command "${command_args[@]}" +``` + +### Comments + +```bash +# Single-line comments: Start with # followed by space +# This is a comment + +# Function documentation (before function definition) +####################################### +# Description of what this function does +# Globals: +# GLOBAL_VAR - Description +# Arguments: +# $1 - First argument description +# $2 - Second argument description (optional) +# Outputs: +# Writes result to stdout +# Returns: +# 0 on success, non-zero on error +####################################### +my_function() { + # Implementation +} + +# Inline comments: Use sparingly, only when necessary +result=$(complex_calculation) # Result in milliseconds + +# TODO comments +# TODO(username): Description of what needs to be done +# FIXME(username): Description of what needs to be fixed +# HACK(username): Description of workaround and why it's needed + +# Section separators for long scripts +#------------------------------------------------------------------------------ +# Configuration Section +#------------------------------------------------------------------------------ + +####################################### +# Database Functions +####################################### +``` + +### Test Constructs + +```bash +# Prefer [[ ]] over [ ] for tests in bash +# [[ ]] is a bash keyword with better behavior: +# - No word splitting +# - No pathname expansion +# - More operators available + +# String comparison +if [[ "$string1" == "$string2" ]]; then # ✓ CORRECT +if [ "$string1" = "$string2" ]; then # ✓ CORRECT (POSIX) +if [ $string1 == $string2 ]; then # ✗ WRONG (word splitting, not POSIX) + +# String matching with patterns +if [[ "$file" == *.txt ]]; then # ✓ CORRECT (pattern matching) +if [[ "$file" =~ \.txt$ ]]; then # ✓ CORRECT (regex) + +# Numeric comparison +if [[ $num -gt 10 ]]; then # ✓ CORRECT +if (( num > 10 )); then # ✓ CORRECT (arithmetic context) + +# File tests +if [[ -f "$file" ]]; then # ✓ CORRECT (regular file) +if [[ -d "$dir" ]]; then # ✓ CORRECT (directory) +if [[ -e "$path" ]]; then # ✓ CORRECT (exists) +if [[ -r "$file" ]]; then # ✓ CORRECT (readable) +if [[ -w "$file" ]]; then # ✓ CORRECT (writable) +if [[ -x "$file" ]]; then # ✓ CORRECT (executable) + +# Logical operators +if [[ condition1 && condition2 ]]; then # ✓ CORRECT (AND) +if [[ condition1 || condition2 ]]; then # ✓ CORRECT (OR) +if [[ ! condition ]]; then # ✓ CORRECT (NOT) + +# Empty/non-empty string +if [[ -z "$var" ]]; then # ✓ CORRECT (empty) +if [[ -n "$var" ]]; then # ✓ CORRECT (non-empty) +``` + +--- + +## Functions + +### Function Best Practices + +```bash +# Good function structure +process_file() { + # 1. Declare local variables + local file="$1" + local output_dir="${2:-.}" # Default to current directory + local result="" + + # 2. Input validation + if [[ ! -f "$file" ]]; then + echo "Error: File not found: $file" >&2 + return 1 + fi + + if [[ ! -d "$output_dir" ]]; then + echo "Error: Output directory not found: $output_dir" >&2 + return 1 + fi + + # 3. Main logic + result=$(perform_operation "$file") + + # 4. Output + echo "$result" > "$output_dir/result.txt" + + # 5. Return status + return 0 +} + +# Use return codes to indicate success/failure +# 0 = success, non-zero = error +validate_input() { + local input="$1" + + if [[ ! "$input" =~ ^[a-zA-Z0-9]+$ ]]; then + return 1 # Invalid input + fi + + return 0 # Valid input +} + +# Usage +if validate_input "$user_input"; then + process "$user_input" +else + echo "Invalid input" >&2 + exit 1 +fi +``` + +### Function Documentation + +```bash +####################################### +# Process a file and generate output +# Globals: +# OUTPUT_FORMAT - Output format (json/xml/csv) +# Arguments: +# $1 - Input file path (required) +# $2 - Output directory (optional, default: .) +# Outputs: +# Writes processed data to stdout +# Writes result file to output directory +# Returns: +# 0 on success +# 1 if file not found +# 2 if processing fails +# Example: +# process_file "input.txt" "/tmp/output" +####################################### +process_file() { + # Implementation +} +``` + +### Local Variables + +```bash +# ALWAYS use local for function variables +bad_function() { + counter=0 # ✗ WRONG - Global variable! +} + +good_function() { + local counter=0 # ✓ CORRECT - Local to function +} + +# Declare local before assignment +good_practice() { + local result + result=$(command_that_might_fail) || return 1 + echo "$result" +} + +# This won't catch command failure: +bad_practice() { + local result=$(command_that_might_fail) # ✗ WRONG + echo "$result" +} +``` + +--- + +## Variables + +### Variable Declaration + +```bash +# Readonly for constants +readonly MAX_RETRIES=3 +declare -r MAX_RETRIES=3 # Alternative syntax + +# Arrays +files=("file1.txt" "file2.txt" "file3.txt") +declare -a files=("file1.txt" "file2.txt") + +# Associative arrays (bash 4+) +declare -A config +config[host]="localhost" +config[port]="8080" + +# Integer variables +declare -i count=0 +count+=1 # Arithmetic operation + +# Export for environment +export DATABASE_URL="postgres://localhost/db" +declare -x DATABASE_URL="postgres://localhost/db" +``` + +### Variable Expansion + +```bash +# Default values +value="${var:-default}" # Use default if var is unset or empty +value="${var-default}" # Use default only if var is unset +value="${var:=default}" # Assign default if var is unset or empty +value="${var+alternative}" # Use alternative if var is set + +# String length +length="${#string}" + +# Substring +substring="${string:0:5}" # First 5 characters +substring="${string:5}" # From 5th character to end + +# Pattern matching (prefix removal) +filename="/path/to/file.txt" +basename="${filename##*/}" # file.txt (remove longest match of */) +dirname="${filename%/*}" # /path/to (remove shortest match of /*) + +# Pattern matching (suffix removal) +file="document.tar.gz" +name="${file%.gz}" # document.tar (remove shortest .gz) +name="${file%%.*}" # document (remove longest .*) + +# Search and replace +string="hello world" +new_string="${string/world/universe}" # First occurrence +new_string="${string//o/0}" # All occurrences +new_string="${string/#hello/hi}" # Prefix match +new_string="${string/%world/earth}" # Suffix match + +# Case modification (bash 4+) +upper="${string^^}" # TO UPPERCASE +lower="${string,,}" # to lowercase +capitalize="${string^}" # Capitalize first letter +``` + +### Command Substitution + +```bash +# Modern syntax: $() +result=$(command) # ✓ CORRECT (preferred) +result=`command` # ✓ CORRECT (old style, avoid) + +# Nested command substitution +outer=$(echo "$(echo inner)") # ✓ CORRECT (easy to nest) +outer=`echo \`echo inner\`` # ✗ WRONG (hard to nest, requires escaping) + +# Process substitution +diff <(command1) <(command2) # Compare outputs +while read -r line; do + echo "$line" +done < <(command) # Read command output +``` + +--- + +## Error Handling + +### Exit Codes + +```bash +# Standard exit codes +readonly EXIT_SUCCESS=0 +readonly EXIT_ERROR=1 +readonly EXIT_INVALID_ARGS=2 +readonly EXIT_MISSING_DEPENDENCY=3 + +# Use meaningful exit codes +validate_args() { + if [[ $# -lt 1 ]]; then + echo "Error: Missing required argument" >&2 + exit "$EXIT_INVALID_ARGS" + fi +} + +# Check command success +if ! command_that_might_fail; then + echo "Error: Command failed" >&2 + exit "$EXIT_ERROR" +fi + +# Alternative syntax +command_that_might_fail || { + echo "Error: Command failed" >&2 + exit "$EXIT_ERROR" +} +``` + +### Error Messages + +```bash +# ALWAYS write errors to stderr +echo "Error: Something went wrong" >&2 + +# Use consistent error message format +error() { + local message="$1" + local code="${2:-$EXIT_ERROR}" + + echo "ERROR: $message" >&2 + return "$code" +} + +# Usage +if ! validate_input "$input"; then + error "Invalid input: $input" "$EXIT_INVALID_ARGS" +fi +``` + +### Trap Handlers + +```bash +# Cleanup on exit +cleanup() { + local exit_code=$? + + # Cleanup operations + [[ -n "${TEMP_DIR:-}" ]] && rm -rf "$TEMP_DIR" + [[ -n "${LOCKFILE:-}" ]] && rm -f "$LOCKFILE" + + # Don't mask errors + exit "$exit_code" +} + +trap cleanup EXIT + +# Handle specific signals +handle_sigterm() { + echo "Received SIGTERM, shutting down..." >&2 + # Graceful shutdown logic + exit 143 # 128 + 15 (SIGTERM) +} + +trap handle_sigterm TERM + +# ERR trap (bash 4.1+) +error_handler() { + local line="$1" + echo "Error on line $line" >&2 +} + +trap 'error_handler ${LINENO}' ERR +``` + +### Defensive Programming + +```bash +# Validate all inputs +process_file() { + local file="$1" + + # Check file exists + if [[ ! -f "$file" ]]; then + echo "Error: File not found: $file" >&2 + return 1 + fi + + # Check file is readable + if [[ ! -r "$file" ]]; then + echo "Error: File not readable: $file" >&2 + return 1 + fi + + # Process file +} + +# Check dependencies before use +check_dependencies() { + local deps=(curl jq awk sed) + local missing=() + + for dep in "${deps[@]}"; do + if ! command -v "$dep" &> /dev/null; then + missing+=("$dep") + fi + done + + if [[ ${#missing[@]} -gt 0 ]]; then + echo "Error: Missing dependencies: ${missing[*]}" >&2 + exit "$EXIT_MISSING_DEPENDENCY" + fi +} + +# Validate environment +if [[ -z "${REQUIRED_VAR:-}" ]]; then + echo "Error: REQUIRED_VAR must be set" >&2 + exit 1 +fi +``` + +--- + +## Input/Output + +### Reading User Input + +```bash +# Simple read +read -rp "Enter your name: " name +echo "Hello, $name" + +# Read with timeout +if read -rt 10 -p "Enter value (10s timeout): " value; then + echo "You entered: $value" +else + echo "Timeout or error" +fi + +# Read password (no echo) +read -rsp "Enter password: " password +echo # New line after password input + +# Read confirmation +confirm() { + local prompt="${1:-Are you sure?}" + local response + + read -rp "$prompt [y/N] " response + case "$response" in + [yY][eE][sS]|[yY]) + return 0 + ;; + *) + return 1 + ;; + esac +} + +# Usage +if confirm "Delete all files?"; then + rm -rf * +fi +``` + +### Reading Files + +```bash +# Read file line by line +while IFS= read -r line; do + echo "Line: $line" +done < file.txt + +# Skip empty lines and comments +while IFS= read -r line || [[ -n "$line" ]]; do + # Skip empty lines + [[ -z "$line" ]] && continue + + # Skip comments + [[ "$line" =~ ^[[:space:]]*# ]] && continue + + echo "Processing: $line" +done < file.txt + +# Read into array +mapfile -t lines < file.txt +# Or +readarray -t lines < file.txt + +# Read with null delimiter (for filenames with spaces) +while IFS= read -r -d '' file; do + echo "File: $file" +done < <(find . -type f -print0) +``` + +### Writing Output + +```bash +# Stdout vs stderr +echo "Normal output" # stdout +echo "Error message" >&2 # stderr + +# Redirect output +command > output.txt # Overwrite +command >> output.txt # Append +command 2> errors.txt # Stderr only +command &> all_output.txt # Both stdout and stderr +command > output.txt 2>&1 # Both (POSIX way) + +# Here documents +cat < file.txt +Line 1 +Line 2 +Variables are expanded: $VAR +EOF + +# Here documents (no expansion) +cat <<'EOF' > file.txt +Line 1 +Line 2 +Variables are NOT expanded: $VAR +EOF + +# Here strings +grep "pattern" <<< "$variable" +``` + +--- + +## Security + +### Input Validation + +```bash +# Validate input format +validate_email() { + local email="$1" + local regex="^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$" + + if [[ "$email" =~ $regex ]]; then + return 0 + else + return 1 + fi +} + +# Sanitize file paths +sanitize_path() { + local path="$1" + + # Remove directory traversal attempts + path="${path//..\/}" + + # Remove leading slashes (if restricting to relative paths) + path="${path#/}" + + echo "$path" +} + +# Whitelist validation (preferred over blacklist) +validate_action() { + local action="$1" + local valid_actions=("start" "stop" "restart" "status") + + for valid in "${valid_actions[@]}"; do + if [[ "$action" == "$valid" ]]; then + return 0 + fi + done + + return 1 +} +``` + +### Command Injection Prevention + +```bash +# NEVER use eval with user input +# ✗ DANGEROUS +eval "$user_input" + +# NEVER concatenate user input into commands +# ✗ DANGEROUS +grep "$user_pattern" file.txt # If pattern contains flags, command injection! + +# ✓ SAFE - Use -- to separate options from arguments +grep -- "$user_pattern" file.txt + +# ✓ SAFE - Use arrays for complex commands +command_args=( + --option1 "$user_value1" + --option2 "$user_value2" +) +command "${command_args[@]}" + +# ✓ SAFE - Use printf %q for shell escaping +safe_value=$(printf %q "$user_input") +eval "command $safe_value" # Now safe, but avoid if possible +``` + +### Temporary Files + +```bash +# Use mktemp for secure temporary files +TEMP_FILE=$(mktemp) || { + echo "Error: Cannot create temp file" >&2 + exit 1 +} + +# Cleanup on exit +trap 'rm -f "$TEMP_FILE"' EXIT + +# Secure temp file (mode 600) +SECURE_TEMP=$(mktemp) +chmod 600 "$SECURE_TEMP" + +# Temporary directory +TEMP_DIR=$(mktemp -d) || { + echo "Error: Cannot create temp directory" >&2 + exit 1 +} + +trap 'rm -rf "$TEMP_DIR"' EXIT +``` + +### Secrets Management + +```bash +# Don't hardcode secrets +# ✗ WRONG +PASSWORD="secret123" + +# ✓ CORRECT - Read from environment +PASSWORD="${DATABASE_PASSWORD:-}" +if [[ -z "$PASSWORD" ]]; then + echo "Error: DATABASE_PASSWORD must be set" >&2 + exit 1 +fi + +# ✓ CORRECT - Read from file +if [[ -f "$HOME/.config/app/password" ]]; then + PASSWORD=$(cat "$HOME/.config/app/password") +fi + +# ✓ CORRECT - Prompt user +read -rsp "Enter password: " PASSWORD +echo + +# Don't log secrets +# ✗ WRONG +echo "Connecting with password: $PASSWORD" + +# ✓ CORRECT +echo "Connecting to database..." + +# Mask secrets in process list +# ✗ WRONG - Password visible in ps +mysql -pSecret123 + +# ✓ CORRECT - Use config file or environment variable +export MYSQL_PWD="$PASSWORD" +mysql + +# Clear secrets from environment when done +unset PASSWORD +``` + +--- + +## Performance + +### Avoid Unnecessary Subshells + +```bash +# ✗ SLOW - Creates subshell +value=$(expr $a + $b) + +# ✓ FAST - Bash arithmetic +value=$((a + b)) + +# ✗ SLOW - External command +value=$(echo "$string" | wc -c) + +# ✓ FAST - Parameter expansion +value=${#string} +``` + +### Use Bash Built-ins + +```bash +# ✗ SLOW - External commands +basename=$(basename "$path") +dirname=$(dirname "$path") + +# ✓ FAST - Parameter expansion +basename="${path##*/}" +dirname="${path%/*}" + +# ✗ SLOW - grep +if echo "$string" | grep -q "pattern"; then + +# ✓ FAST - Bash regex +if [[ "$string" =~ pattern ]]; then + +# ✗ SLOW - awk/cut +field=$(echo "$line" | awk '{print $3}') + +# ✓ FAST - Read into array +read -ra fields <<< "$line" +field="${fields[2]}" +``` + +### Efficient Loops + +```bash +# ✗ SLOW - Running external command in loop +for i in {1..1000}; do + result=$(date +%s) +done + +# ✓ FAST - Call once +timestamp=$(date +%s) +for i in {1..1000}; do + result=$timestamp +done + +# ✗ SLOW - Multiple passes +cat file | grep pattern | sort | uniq + +# ✓ FAST - Single pass where possible +grep pattern file | sort -u +``` + +--- + +## Documentation + +### Script Header + +```bash +#!/usr/bin/env bash +# +# backup.sh - Automated backup script +# +# Description: +# Creates incremental backups of specified directories +# to a remote server using rsync. +# +# Usage: +# backup.sh [OPTIONS] +# +# Options: +# -h, --help Show this help message +# -v, --verbose Enable verbose output +# -n, --dry-run Show what would be done +# -c, --config FILE Use alternative config file +# +# Arguments: +# source Directory to backup +# destination Remote destination (user@host:/path) +# +# Examples: +# backup.sh /home/user user@backup:/backups/ +# backup.sh -v -c custom.conf /data remote:/store/ +# +# Dependencies: +# - rsync >= 3.0 +# - ssh +# +# Environment Variables: +# BACKUP_CONFIG Path to configuration file +# BACKUP_VERBOSE Enable verbose mode if set +# +# Exit Codes: +# 0 Success +# 1 General error +# 2 Invalid arguments +# 3 Missing dependency +# 4 Backup failed +# +# Author: Your Name +# Version: 1.2.0 +# Date: 2024-01-01 +# License: MIT +# +``` + +### Inline Documentation + +```bash +# Document complex logic +# This algorithm uses binary search to find the optimal value +# Time complexity: O(log n) +# Space complexity: O(1) + +# Explain workarounds +# HACK: Sleep needed because API has rate limiting without proper headers +sleep 1 + +# Document assumptions +# Assumes file is in CSV format with header row + +# Link to external resources +# See: https://docs.example.com/api for API documentation +``` + +### README and CHANGELOG + +Every non-trivial script should have: + +1. **README.md** - Installation, usage, examples +2. **CHANGELOG.md** - Version history +3. **LICENSE** - Licensing information + +--- + +## Testing + +### Unit Tests with BATS + +```bash +# test/backup.bats +#!/usr/bin/env bats + +# Setup runs before each test +setup() { + # Create temp directory for tests + TEST_DIR="$(mktemp -d)" + export TEST_DIR +} + +# Teardown runs after each test +teardown() { + rm -rf "$TEST_DIR" +} + +@test "backup creates archive" { + run ./backup.sh "$TEST_DIR" backup.tar.gz + [ "$status" -eq 0 ] + [ -f backup.tar.gz ] +} + +@test "backup fails with invalid source" { + run ./backup.sh /nonexistent backup.tar.gz + [ "$status" -eq 1 ] + [ "${lines[0]}" = "Error: Source directory not found" ] +} + +@test "backup validates dependencies" { + # Mock missing dependency + function tar() { return 127; } + export -f tar + + run ./backup.sh "$TEST_DIR" backup.tar.gz + [ "$status" -eq 3 ] +} +``` + +### Integration Tests + +```bash +# integration_test.sh +#!/usr/bin/env bash +set -euo pipefail + +# Test end-to-end workflow +test_full_workflow() { + echo "Testing full workflow..." + + # Setup + local test_dir="/tmp/test_$$" + mkdir -p "$test_dir" + + # Execute + ./script.sh create "$test_dir/output" + ./script.sh process "$test_dir/output" + ./script.sh verify "$test_dir/output" + + # Verify + if [[ -f "$test_dir/output/result.txt" ]]; then + echo "✓ Full workflow test passed" + rm -rf "$test_dir" + return 0 + else + echo "✗ Full workflow test failed" + rm -rf "$test_dir" + return 1 + fi +} + +# Run all tests +main() { + local failed=0 + + test_full_workflow || ((failed++)) + + if [[ $failed -eq 0 ]]; then + echo "All tests passed" + exit 0 + else + echo "$failed test(s) failed" + exit 1 + fi +} + +main +``` + +--- + +## Maintenance + +### Version Control + +```bash +# Include version in script +readonly VERSION="1.2.0" + +show_version() { + echo "$SCRIPT_NAME version $VERSION" +} + +# Semantic versioning: MAJOR.MINOR.PATCH +# - MAJOR: Breaking changes +# - MINOR: New features (backward compatible) +# - PATCH: Bug fixes +``` + +### Deprecation + +```bash +# Deprecation warning +deprecated_function() { + echo "Warning: deprecated_function is deprecated, use new_function instead" >&2 + new_function "$@" +} + +# Version-based deprecation +if [[ "${SCRIPT_VERSION%%.*}" -ge 2 ]]; then + # Remove deprecated feature in version 2.0 + unset deprecated_function +fi +``` + +### Backward Compatibility + +```bash +# Support old parameter names +if [[ -n "${OLD_PARAM:-}" && -z "${NEW_PARAM:-}" ]]; then + echo "Warning: OLD_PARAM is deprecated, use NEW_PARAM" >&2 + NEW_PARAM="$OLD_PARAM" +fi + +# Support multiple config file locations +for config in "$XDG_CONFIG_HOME/app/config" "$HOME/.config/app/config" "$HOME/.apprc"; do + if [[ -f "$config" ]]; then + CONFIG_FILE="$config" + break + fi +done +``` + +--- + +## Summary Checklist + +Before considering a bash script production-ready: + +- [ ] Passes ShellCheck with no warnings +- [ ] Uses `set -euo pipefail` +- [ ] All variables quoted properly +- [ ] Functions use local variables +- [ ] Has usage/help message +- [ ] Validates all inputs +- [ ] Checks dependencies +- [ ] Proper error messages (to stderr) +- [ ] Uses meaningful exit codes +- [ ] Includes cleanup trap +- [ ] Has inline documentation +- [ ] Follows consistent style +- [ ] Has unit tests (BATS) +- [ ] Has integration tests +- [ ] Tested on target platforms +- [ ] Has README documentation +- [ ] Version controlled (git) +- [ ] Reviewed by peer + +**Additional for production:** +- [ ] Has CI/CD pipeline +- [ ] Logging implemented +- [ ] Monitoring/alerting configured +- [ ] Security reviewed +- [ ] Performance tested +- [ ] Disaster recovery plan +- [ ] Runbook/operational docs + +This ensures professional, maintainable, and robust bash scripts. diff --git a/skills/bash-master/references/patterns_antipatterns.md b/skills/bash-master/references/patterns_antipatterns.md new file mode 100644 index 0000000..18437c0 --- /dev/null +++ b/skills/bash-master/references/patterns_antipatterns.md @@ -0,0 +1,992 @@ +# Common Bash Patterns and Anti-Patterns + +Collection of proven patterns and common mistakes in bash scripting with explanations and solutions. + +--- + +## Table of Contents + +1. [Variable Handling](#variable-handling) +2. [Command Execution](#command-execution) +3. [File Operations](#file-operations) +4. [String Processing](#string-processing) +5. [Arrays and Loops](#arrays-and-loops) +6. [Conditionals and Tests](#conditionals-and-tests) +7. [Functions](#functions) +8. [Error Handling](#error-handling) +9. [Process Management](#process-management) +10. [Security Patterns](#security-patterns) + +--- + +## 🚨 CRITICAL GUIDELINES + +### Windows File Path Requirements + +**MANDATORY: Always Use Backslashes on Windows for File Paths** + +When using Edit or Write tools on Windows, you MUST use backslashes (`\`) in file paths, NOT forward slashes (`/`). + +**Examples:** +- ❌ WRONG: `D:/repos/project/file.tsx` +- ✅ CORRECT: `D:\repos\project\file.tsx` + +This applies to: +- Edit tool file_path parameter +- Write tool file_path parameter +- All file operations on Windows systems + + +### Documentation Guidelines + +**NEVER create new documentation files unless explicitly requested by the user.** + +- **Priority**: Update existing README.md files rather than creating new documentation +- **Repository cleanliness**: Keep repository root clean - only README.md unless user requests otherwise +- **Style**: Documentation should be concise, direct, and professional - avoid AI-generated tone +- **User preference**: Only create additional .md files when user specifically asks for documentation + + +--- + +## Variable Handling + +### Pattern: Safe Variable Expansion + +```bash +# ✓ GOOD: Always quote variables +echo "$variable" +cp "$source" "$destination" +rm -rf "$directory" + +# ✗ BAD: Unquoted variables +echo $variable # Word splitting and globbing +cp $source $destination # Breaks with spaces +rm -rf $directory # VERY DANGEROUS unquoted +``` + +**Why:** Unquoted variables undergo word splitting and pathname expansion, leading to unexpected behavior. + +### Pattern: Default Values + +```bash +# ✓ GOOD: Use parameter expansion for defaults +timeout="${TIMEOUT:-30}" +config="${CONFIG_FILE:-$HOME/.config/app.conf}" + +# ✗ BAD: Manual check +if [ -z "$TIMEOUT" ]; then + timeout=30 +else + timeout="$TIMEOUT" +fi +``` + +**Why:** Parameter expansion is concise, readable, and handles edge cases correctly. + +### Anti-Pattern: Confusing Assignment and Comparison + +```bash +# ✗ VERY BAD: Using = instead of == +if [ "$var" = "value" ]; then # Assignment in POSIX test! + echo "Match" +fi + +# ✓ GOOD: Use == or = correctly +if [[ "$var" == "value" ]]; then # Comparison in bash + echo "Match" +fi + +# ✓ GOOD: POSIX-compliant +if [ "$var" = "value" ]; then # Single = is correct in [ ] + echo "Match" +fi +``` + +**Why:** In `[[ ]]`, both `=` and `==` work. In `[ ]`, only `=` is POSIX-compliant. + +### Anti-Pattern: Unset Variable Access + +```bash +# ✗ BAD: Accessing undefined variables +echo "Value: $undefined_variable" # Silent error, prints "Value: " + +# ✓ GOOD: Use set -u +set -u +echo "Value: $undefined_variable" # Error: undefined_variable: unbound variable + +# ✓ GOOD: Provide default +echo "Value: ${undefined_variable:-default}" +``` + +**Why:** `set -u` catches typos and logic errors early. + +--- + +## Command Execution + +### Pattern: Check Command Existence + +```bash +# ✓ GOOD: Use command -v +if command -v jq &> /dev/null; then + echo "jq is installed" +else + echo "jq is not installed" >&2 + exit 1 +fi + +# ✗ BAD: Using which +if which jq; then # Deprecated, not POSIX + echo "jq is installed" +fi + +# ✗ BAD: Using type +if type jq; then # Verbose output + echo "jq is installed" +fi +``` + +**Why:** `command -v` is POSIX-compliant, silent, and reliable. + +### Pattern: Command Substitution + +```bash +# ✓ GOOD: Modern syntax with $() +result=$(command arg1 arg2) +timestamp=$(date +%s) + +# ✗ BAD: Backticks (hard to nest) +result=`command arg1 arg2` +timestamp=`date +%s` + +# ✓ GOOD: Nested substitution +result=$(echo "Outer: $(echo "Inner")") + +# ✗ BAD: Nested backticks (requires escaping) +result=`echo "Outer: \`echo \"Inner\"\`"` +``` + +**Why:** `$()` is easier to read, nest, and maintain. + +### Anti-Pattern: Useless Use of Cat + +```bash +# ✗ BAD: UUOC (Useless Use of Cat) +cat file.txt | grep "pattern" + +# ✓ GOOD: Direct input +grep "pattern" file.txt + +# ✗ BAD: Multiple cats +cat file1 | grep pattern | cat | sort | cat + +# ✓ GOOD: Direct pipeline +grep pattern file1 | sort +``` + +**Why:** Unnecessary `cat` wastes resources and adds extra processes. + +### Anti-Pattern: Using ls in Scripts + +```bash +# ✗ BAD: Parsing ls output +for file in $(ls *.txt); do + echo "$file" +done + +# ✓ GOOD: Use globbing +for file in *.txt; do + [[ -f "$file" ]] || continue # Skip if no matches + echo "$file" +done + +# ✗ BAD: Counting files with ls +count=$(ls -1 | wc -l) + +# ✓ GOOD: Use array +files=(*) +count=${#files[@]} +``` + +**Why:** `ls` output is meant for humans, not scripts. Parsing it breaks with spaces, newlines, etc. + +--- + +## File Operations + +### Pattern: Safe File Reading + +```bash +# ✓ GOOD: Preserve leading/trailing whitespace and backslashes +while IFS= read -r line; do + echo "Line: $line" +done < file.txt + +# ✗ BAD: Without IFS= (strips leading/trailing whitespace) +while read -r line; do + echo "Line: $line" +done < file.txt + +# ✗ BAD: Without -r (interprets backslashes) +while IFS= read line; do + echo "Line: $line" +done < file.txt +``` + +**Why:** `IFS=` prevents trimming, `-r` prevents backslash interpretation. + +### Pattern: Null-Delimited Files + +```bash +# ✓ GOOD: For filenames with special characters +find . -name "*.txt" -print0 | while IFS= read -r -d '' file; do + echo "Processing: $file" +done + +# Or with mapfile (bash 4+) +mapfile -d '' -t files < <(find . -name "*.txt" -print0) +for file in "${files[@]}"; do + echo "Processing: $file" +done + +# ✗ BAD: Newline-delimited (breaks with newlines in filenames) +find . -name "*.txt" | while IFS= read -r file; do + echo "Processing: $file" +done +``` + +**Why:** Filenames can contain any character except null and slash. + +### Anti-Pattern: Testing File Existence Incorrectly + +```bash +# ✗ BAD: Using ls to test existence +if ls file.txt &> /dev/null; then + echo "File exists" +fi + +# ✓ GOOD: Use test operators +if [[ -f file.txt ]]; then + echo "File exists" +fi + +# ✓ GOOD: Different tests +[[ -e path ]] # Exists (file or directory) +[[ -f file ]] # Regular file +[[ -d dir ]] # Directory +[[ -L link ]] # Symbolic link +[[ -r file ]] # Readable +[[ -w file ]] # Writable +[[ -x file ]] # Executable +``` + +**Why:** Test operators are the correct, efficient way to check file properties. + +### Pattern: Temporary Files + +```bash +# ✓ GOOD: Secure temporary file +temp_file=$(mktemp) +trap 'rm -f "$temp_file"' EXIT + +# Use temp file +echo "data" > "$temp_file" + +# ✗ BAD: Insecure temp file +temp_file="/tmp/myapp.$$" +echo "data" > "$temp_file" +# No cleanup! + +# ✓ GOOD: Temporary directory +temp_dir=$(mktemp -d) +trap 'rm -rf "$temp_dir"' EXIT +``` + +**Why:** `mktemp` creates secure, unique files and prevents race conditions. + +--- + +## String Processing + +### Pattern: String Manipulation with Parameter Expansion + +```bash +# ✓ GOOD: Use bash parameter expansion +filename="document.tar.gz" +basename="${filename%%.*}" # document +extension="${filename##*.}" # gz +name="${filename%.gz}" # document.tar + +# ✗ BAD: Using external commands +basename=$(echo "$filename" | sed 's/\..*$//') +extension=$(echo "$filename" | awk -F. '{print $NF}') +``` + +**Why:** Parameter expansion is faster and doesn't spawn processes. + +### Pattern: String Comparison + +```bash +# ✓ GOOD: Use [[ ]] for strings +if [[ "$string1" == "$string2" ]]; then + echo "Equal" +fi + +# ✓ GOOD: Pattern matching +if [[ "$filename" == *.txt ]]; then + echo "Text file" +fi + +# ✓ GOOD: Regex matching +if [[ "$email" =~ ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]]; then + echo "Valid email" +fi + +# ✗ BAD: Using grep for simple string check +if echo "$string" | grep -q "substring"; then + echo "Found" +fi + +# ✓ GOOD: Use substring matching +if [[ "$string" == *"substring"* ]]; then + echo "Found" +fi +``` + +**Why:** `[[ ]]` is bash-native, faster, and more readable. + +### Anti-Pattern: Word Splitting Issues + +```bash +# ✗ BAD: Unquoted expansion with spaces +var="file1.txt file2.txt" +for file in $var; do # Splits on spaces! + echo "$file" # file1.txt, then file2.txt +done + +# ✓ GOOD: Use array +files=("file1.txt" "file2.txt") +for file in "${files[@]}"; do + echo "$file" +done + +# ✗ BAD: Word splitting in command arguments +file="my file.txt" +rm $file # Tries to remove "my" and "file.txt"! + +# ✓ GOOD: Quote variables +rm "$file" +``` + +**Why:** Word splitting on spaces is a major source of bugs. + +--- + +## Arrays and Loops + +### Pattern: Array Declaration and Use + +```bash +# ✓ GOOD: Array declaration +files=("file1.txt" "file2.txt" "file 3.txt") + +# ✓ GOOD: Array expansion (each element quoted) +for file in "${files[@]}"; do + echo "$file" +done + +# ✗ BAD: Unquoted array expansion +for file in ${files[@]}; do # Word splitting! + echo "$file" +done + +# ✓ GOOD: Add to array +files+=("file4.txt") + +# ✓ GOOD: Array length +echo "Count: ${#files[@]}" + +# ✓ GOOD: Array indices +for i in "${!files[@]}"; do + echo "File $i: ${files[$i]}" +done +``` + +**Why:** Proper array handling prevents word splitting and globbing issues. + +### Pattern: Reading Command Output into Array + +```bash +# ✓ GOOD: mapfile/readarray (bash 4+) +mapfile -t lines < file.txt + +# ✓ GOOD: With command substitution +mapfile -t files < <(find . -name "*.txt") + +# ✗ BAD: Word splitting +files=($(find . -name "*.txt")) # Breaks with spaces in filenames! + +# ✓ GOOD: Alternative (POSIX-compatible) +while IFS= read -r file; do + files+=("$file") +done < <(find . -name "*.txt") +``` + +**Why:** `mapfile` is efficient and handles special characters correctly. + +### Anti-Pattern: C-Style For Loops for Arrays + +```bash +# ✗ BAD: C-style loop for arrays +for ((i=0; i<${#files[@]}; i++)); do + echo "${files[$i]}" +done + +# ✓ GOOD: For-in loop +for file in "${files[@]}"; do + echo "$file" +done + +# ✓ ACCEPTABLE: When you need the index +for i in "${!files[@]}"; do + echo "Index $i: ${files[$i]}" +done +``` + +**Why:** For-in loops are simpler and less error-prone. + +### Pattern: Loop over Range + +```bash +# ✓ GOOD: Brace expansion +for i in {1..10}; do + echo "$i" +done + +# ✓ GOOD: With variables (bash 4+) +start=1 +end=10 +for i in $(seq $start $end); do + echo "$i" +done + +# ✓ GOOD: C-style (arithmetic) +for ((i=1; i<=10; i++)); do + echo "$i" +done + +# ✗ BAD: Using seq in a loop unnecessarily +for i in $(seq 1 1000000); do # Creates huge string in memory! + echo "$i" +done + +# ✓ GOOD: Use C-style for large ranges +for ((i=1; i<=1000000; i++)); do + echo "$i" +done +``` + +**Why:** Choose the right loop construct based on the use case. + +--- + +## Conditionals and Tests + +### Pattern: File Tests + +```bash +# ✓ GOOD: Use appropriate test +if [[ -f "$file" ]]; then # Regular file +if [[ -d "$dir" ]]; then # Directory +if [[ -e "$path" ]]; then # Exists (any type) +if [[ -L "$link" ]]; then # Symbolic link +if [[ -r "$file" ]]; then # Readable +if [[ -w "$file" ]]; then # Writable +if [[ -x "$file" ]]; then # Executable +if [[ -s "$file" ]]; then # Non-empty file + +# ✗ BAD: Incorrect test +if [[ -e "$file" ]]; then # Exists, but could be directory! + cat "$file" # Fails if directory +fi + +# ✓ GOOD: Specific test +if [[ -f "$file" ]]; then + cat "$file" +fi +``` + +**Why:** Use the most specific test for your use case. + +### Pattern: Numeric Comparison + +```bash +# ✓ GOOD: Arithmetic context +if (( num > 10 )); then + echo "Greater than 10" +fi + +# ✓ GOOD: Test operator +if [[ $num -gt 10 ]]; then + echo "Greater than 10" +fi + +# ✗ BAD: String comparison for numbers +if [[ "$num" > "10" ]]; then # Lexicographic comparison! + echo "Greater than 10" # "9" > "10" is true! +fi +``` + +**Why:** Use numeric comparison operators for numbers. + +### Anti-Pattern: Testing Boolean Strings + +```bash +# ✗ BAD: Comparing to string "true" +if [[ "$flag" == "true" ]]; then + do_something +fi + +# ✓ GOOD: Use boolean variable directly +flag=false # or true + +if $flag; then + do_something +fi + +# ✓ BETTER: Use integers for flags +flag=0 # false +flag=1 # true + +if (( flag )); then + do_something +fi + +# ✓ GOOD: For command success/failure +if command; then + echo "Success" +fi +``` + +**Why:** Boolean strings are error-prone; use actual booleans or return codes. + +### Pattern: Multiple Conditions + +```bash +# ✓ GOOD: Logical operators +if [[ condition1 && condition2 ]]; then + echo "Both true" +fi + +if [[ condition1 || condition2 ]]; then + echo "At least one true" +fi + +if [[ ! condition ]]; then + echo "False" +fi + +# ✗ BAD: Separate tests +if [ condition1 -a condition2 ]; then # Deprecated + echo "Both true" +fi + +# ✗ BAD: Nested ifs for AND +if [[ condition1 ]]; then + if [[ condition2 ]]; then + echo "Both true" + fi +fi +``` + +**Why:** `&&` and `||` in `[[ ]]` are clearer and recommended. + +--- + +## Functions + +### Pattern: Function Return Values + +```bash +# ✓ GOOD: Return status, output to stdout +get_value() { + local value="result" + + if [[ -n "$value" ]]; then + echo "$value" + return 0 + else + return 1 + fi +} + +# Usage +if result=$(get_value); then + echo "Got: $result" +else + echo "Failed" +fi + +# ✗ BAD: Using return for data +get_value() { + return 42 # Can only return 0-255! +} +result=$? # Gets 42, but limited range +``` + +**Why:** `return` is for exit status (0-255), not data. Output to stdout for data. + +### Pattern: Local Variables in Functions + +```bash +# ✓ GOOD: Declare local variables +my_function() { + local arg="$1" + local result="" + + result=$(process "$arg") + echo "$result" +} + +# ✗ BAD: Global variables +my_function() { + arg="$1" # Pollutes global namespace! + result="" # Global variable! + + result=$(process "$arg") + echo "$result" +} +``` + +**Why:** Local variables prevent unexpected side effects. + +### Anti-Pattern: Capturing Local Command Failure + +```bash +# ✗ BAD: Local declaration masks command failure +my_function() { + local result=$(command_that_fails) # $? is from 'local', not 'command'! + echo "$result" +} + +# ✓ GOOD: Separate declaration and assignment +my_function() { + local result + result=$(command_that_fails) || return 1 + echo "$result" +} + +# ✓ GOOD: Check command separately +my_function() { + local result + + if ! result=$(command_that_fails); then + return 1 + fi + + echo "$result" +} +``` + +**Why:** Combining `local` and command substitution hides command failure. + +--- + +## Error Handling + +### Pattern: Check Command Success + +```bash +# ✓ GOOD: Direct check +if ! command; then + echo "Command failed" >&2 + exit 1 +fi + +# ✓ GOOD: With logical operator +command || { + echo "Command failed" >&2 + exit 1 +} + +# ✓ GOOD: Capture output and check +if ! output=$(command 2>&1); then + echo "Command failed: $output" >&2 + exit 1 +fi + +# ✗ BAD: Not checking status +command # What if it fails? +next_command +``` + +**Why:** Always check if commands succeed unless failure is acceptable. + +### Pattern: Error Messages to stderr + +```bash +# ✓ GOOD: Errors to stderr +echo "Error: Invalid argument" >&2 + +# ✗ BAD: Errors to stdout +echo "Error: Invalid argument" + +# ✓ GOOD: Error function +error() { + echo "ERROR: $*" >&2 +} + +error "Something went wrong" +``` + +**Why:** stderr is for errors, stdout is for data output. + +### Pattern: Cleanup on Exit + +```bash +# ✓ GOOD: Trap for cleanup +temp_file=$(mktemp) + +cleanup() { + rm -f "$temp_file" +} + +trap cleanup EXIT + +# Do work with temp_file + +# ✗ BAD: Manual cleanup (might not run) +temp_file=$(mktemp) + +# Do work + +rm -f "$temp_file" # Doesn't run if script exits early! +``` + +**Why:** Trap ensures cleanup runs on exit, even on errors. + +### Anti-Pattern: Silencing Errors + +```bash +# ✗ BAD: Silencing errors +command 2>/dev/null # What if it fails? +next_command + +# ✓ GOOD: Check status even if silencing output +if ! command 2>/dev/null; then + echo "Command failed" >&2 + exit 1 +fi + +# ✓ ACCEPTABLE: When failure is expected and acceptable +if command 2>/dev/null; then + echo "Command succeeded" +else + echo "Command failed (expected)" +fi +``` + +**Why:** Silencing errors without checking status leads to silent failures. + +--- + +## Process Management + +### Pattern: Background Jobs + +```bash +# ✓ GOOD: Track background jobs +long_running_task & +pid=$! + +# Wait for completion +if wait "$pid"; then + echo "Task completed successfully" +else + echo "Task failed" >&2 +fi + +# ✓ GOOD: Multiple background jobs +job1 & +pid1=$! +job2 & +pid2=$! + +wait "$pid1" "$pid2" +``` + +**Why:** Proper job management prevents zombie processes. + +### Pattern: Timeout for Commands + +```bash +# ✓ GOOD: Use timeout command (if available) +if timeout 30 long_running_command; then + echo "Completed within timeout" +else + echo "Timed out or failed" >&2 +fi + +# ✓ GOOD: Manual timeout implementation +timeout_command() { + local timeout=$1 + shift + + "$@" & + local pid=$! + + ( sleep "$timeout"; kill "$pid" 2>/dev/null ) & + local killer=$! + + if wait "$pid" 2>/dev/null; then + kill "$killer" 2>/dev/null + wait "$killer" 2>/dev/null + return 0 + else + return 1 + fi +} + +timeout_command 30 long_running_command +``` + +**Why:** Prevents scripts from hanging indefinitely. + +### Anti-Pattern: Killing Processes Unsafely + +```bash +# ✗ BAD: kill -9 immediately +kill -9 "$pid" + +# ✓ GOOD: Graceful shutdown first +kill -TERM "$pid" +sleep 2 + +if kill -0 "$pid" 2>/dev/null; then + echo "Process still running, forcing..." >&2 + kill -KILL "$pid" +fi + +# ✓ GOOD: With timeout +graceful_kill() { + local pid=$1 + local timeout=${2:-10} + + kill -TERM "$pid" 2>/dev/null || return 0 + + for ((i=0; i/dev/null; then + return 0 + fi + sleep 1 + done + + echo "Forcing kill of $pid" >&2 + kill -KILL "$pid" 2>/dev/null +} +``` + +**Why:** SIGTERM allows graceful shutdown; SIGKILL should be last resort. + +--- + +## Security Patterns + +### Pattern: Input Validation + +```bash +# ✓ GOOD: Whitelist validation +validate_action() { + local action=$1 + case "$action" in + start|stop|restart|status) + return 0 + ;; + *) + echo "Error: Invalid action: $action" >&2 + return 1 + ;; + esac +} + +# ✗ BAD: No validation +action="$1" +systemctl "$action" myservice # User can pass arbitrary commands! + +# ✓ GOOD: Validate first +if validate_action "$1"; then + systemctl "$1" myservice +else + exit 1 +fi +``` + +**Why:** Whitelist validation prevents command injection. + +### Pattern: Avoid eval + +```bash +# ✗ BAD: eval with user input +eval "$user_command" # DANGEROUS! + +# ✓ GOOD: Use arrays +command_args=("$arg1" "$arg2" "$arg3") +command "${command_args[@]}" + +# ✗ BAD: Dynamic variable names +eval "var_$name=value" + +# ✓ GOOD: Associative arrays (bash 4+) +declare -A vars +vars[$name]="value" +``` + +**Why:** `eval` with user input is a security vulnerability. + +### Pattern: Safe PATH + +```bash +# ✓ GOOD: Set explicit PATH +export PATH="/usr/local/bin:/usr/bin:/bin" + +# ✓ GOOD: Use absolute paths for critical commands +/usr/bin/rm -rf "$directory" + +# ✗ BAD: Trusting user's PATH +rm -rf "$directory" # What if there's a malicious 'rm' in PATH? +``` + +**Why:** Prevents PATH injection attacks. + +--- + +## Summary + +**Most Critical Patterns:** + +1. Always quote variable expansions: `"$var"` +2. Use `set -euo pipefail` for safety +3. Prefer `[[ ]]` over `[ ]` in bash +4. Use arrays for lists: `"${array[@]}"` +5. Check command success: `if ! command; then` +6. Use local variables in functions +7. Errors to stderr: `echo "Error" >&2` +8. Use `mktemp` for temporary files +9. Cleanup with traps: `trap cleanup EXIT` +10. Validate all user input + +**Most Dangerous Anti-Patterns:** + +1. Unquoted variables: `$var` +2. Parsing `ls` output +3. Using `eval` with user input +4. Silencing errors without checking +5. Not using `set -u` or defaults +6. Global variables in functions +7. Word splitting on filenames +8. Testing strings with `>` for numbers +9. `kill -9` without trying graceful shutdown +10. Trusting user PATH + +Following these patterns and avoiding anti-patterns will result in robust, secure, and maintainable bash scripts. diff --git a/skills/bash-master/references/platform_specifics.md b/skills/bash-master/references/platform_specifics.md new file mode 100644 index 0000000..b8efbef --- /dev/null +++ b/skills/bash-master/references/platform_specifics.md @@ -0,0 +1,1080 @@ +# Platform-Specific Bash Scripting + +Comprehensive guide to handling platform differences in bash scripts across Linux, macOS, Windows (Git Bash/WSL), and containers. + +--- + + +## ⚠️ WINDOWS GIT BASH / MINGW PATH CONVERSION + +**CRITICAL REFERENCE:** For complete Windows Git Bash path conversion and shell detection guidance, see: + +**📄 [windows-git-bash-paths.md](./windows-git-bash-paths.md)** + +This comprehensive guide covers: +- **Automatic path conversion behavior** (Unix → Windows) +- **MSYS_NO_PATHCONV and MSYS2_ARG_CONV_EXCL** usage +- **cygpath** manual conversion tool +- **Shell detection methods** ($OSTYPE, uname, $MSYSTEM) +- **Claude Code specific issues** (#2602 snapshot path conversion) +- **Common problems and solutions** +- **Cross-platform scripting patterns** + +**Git Bash path conversion is the #1 source of Windows bash scripting issues.** Always consult the dedicated guide when working with Windows/Git Bash. + +--- + +## WARNING: WINDOWS GIT BASH / MINGW PATH CONVERSION + +**CRITICAL REFERENCE:** For complete Windows Git Bash path conversion and shell detection guidance, see: + +**[windows-git-bash-paths.md](./windows-git-bash-paths.md)** + +This comprehensive guide covers: +- **Automatic path conversion behavior** (Unix to Windows) +- **MSYS_NO_PATHCONV and MSYS2_ARG_CONV_EXCL** usage +- **cygpath** manual conversion tool +- **Shell detection methods** ($OSTYPE, uname, $MSYSTEM) +- **Claude Code specific issues** (#2602 snapshot path conversion) +- **Common problems and solutions** +- **Cross-platform scripting patterns** + +**Git Bash path conversion is the #1 source of Windows bash scripting issues.** Always consult the dedicated guide when working with Windows/Git Bash. + +--- + +## Table of Contents + +1. [Platform Detection](#platform-detection) +2. [Linux Specifics](#linux-specifics) +3. [macOS Specifics](#macos-specifics) +4. [Windows (Git Bash)](#windows-git-bash) - **See windows-git-bash-paths.md for complete guide** - **See windows-git-bash-paths.md for complete guide** +5. [Windows (WSL)](#windows-wsl) +6. [Container Environments](#container-environments) +7. [Cross-Platform Patterns](#cross-platform-patterns) +8. [Command Compatibility Matrix](#command-compatibility-matrix) + +--- + +## 🚨 CRITICAL GUIDELINES + +### Windows File Path Requirements + +**MANDATORY: Always Use Backslashes on Windows for File Paths** + +When using Edit or Write tools on Windows, you MUST use backslashes (`\`) in file paths, NOT forward slashes (`/`). + +**Examples:** +- ❌ WRONG: `D:/repos/project/file.tsx` +- ✅ CORRECT: `D:\repos\project\file.tsx` + +This applies to: +- Edit tool file_path parameter +- Write tool file_path parameter +- All file operations on Windows systems + + +### Documentation Guidelines + +**NEVER create new documentation files unless explicitly requested by the user.** + +- **Priority**: Update existing README.md files rather than creating new documentation +- **Repository cleanliness**: Keep repository root clean - only README.md unless user requests otherwise +- **Style**: Documentation should be concise, direct, and professional - avoid AI-generated tone +- **User preference**: Only create additional .md files when user specifically asks for documentation + + +--- + +## Platform Detection + +### Comprehensive Detection Script + +```bash +#!/usr/bin/env bash + +detect_os() { + case "$OSTYPE" in + linux-gnu*) + if grep -qi microsoft /proc/version 2>/dev/null; then + echo "wsl" + else + echo "linux" + fi + ;; + darwin*) + echo "macos" + ;; + msys*|mingw*|cygwin*) + echo "gitbash" + ;; + *) + echo "unknown" + ;; + esac +} + +detect_distro() { + # Only for Linux + if [[ -f /etc/os-release ]]; then + # shellcheck source=/dev/null + source /etc/os-release + echo "$ID" + elif [[ -f /etc/redhat-release ]]; then + echo "rhel" + elif [[ -f /etc/debian_version ]]; then + echo "debian" + else + echo "unknown" + fi +} + +detect_container() { + if [[ -f /.dockerenv ]]; then + echo "docker" + elif grep -q docker /proc/1/cgroup 2>/dev/null; then + echo "docker" + elif [[ -n "$KUBERNETES_SERVICE_HOST" ]]; then + echo "kubernetes" + else + echo "none" + fi +} + +# Usage +OS=$(detect_os) +DISTRO=$(detect_distro) +CONTAINER=$(detect_container) + +echo "OS: $OS" +echo "Distro: $DISTRO" +echo "Container: $CONTAINER" +``` + +### Environment Variables for Detection + +```bash +# Check various environment indicators +check_environment() { + echo "OSTYPE: $OSTYPE" + echo "MACHTYPE: $MACHTYPE" + echo "HOSTTYPE: $HOSTTYPE" + + # Kernel info + uname -s # Operating system name + uname -r # Kernel release + uname -m # Machine hardware + uname -p # Processor type + + # More detailed + uname -a # All information +} + +# Platform-specific variables +# Linux: OSTYPE=linux-gnu +# macOS: OSTYPE=darwin20.0 +# Git Bash: OSTYPE=msys +# Cygwin: OSTYPE=cygwin +# WSL: OSTYPE=linux-gnu (but with Microsoft in /proc/version) +``` + +--- + +## Linux Specifics + +### Linux-Only Features + +```bash +# /proc filesystem +get_process_info() { + local pid=$1 + + if [[ -d "/proc/$pid" ]]; then + echo "Command: $(cat /proc/$pid/cmdline | tr '\0' ' ')" + echo "Working dir: $(readlink /proc/$pid/cwd)" + echo "Executable: $(readlink /proc/$pid/exe)" + fi +} + +# systemd +check_systemd() { + if command -v systemctl &> /dev/null; then + systemctl status my-service + systemctl is-active my-service + systemctl is-enabled my-service + fi +} + +# cgroups +check_cgroups() { + if [[ -d /sys/fs/cgroup ]]; then + cat /sys/fs/cgroup/memory/memory.limit_in_bytes + fi +} + +# inotify for file watching +watch_directory() { + if command -v inotifywait &> /dev/null; then + inotifywait -m -r -e modify,create,delete /path/to/watch + fi +} +``` + +### Distribution-Specific Commands + +```bash +# Package management +install_package() { + local package=$1 + + if command -v apt-get &> /dev/null; then + # Debian/Ubuntu + sudo apt-get update + sudo apt-get install -y "$package" + elif command -v yum &> /dev/null; then + # RHEL/CentOS + sudo yum install -y "$package" + elif command -v dnf &> /dev/null; then + # Fedora + sudo dnf install -y "$package" + elif command -v pacman &> /dev/null; then + # Arch + sudo pacman -S --noconfirm "$package" + elif command -v zypper &> /dev/null; then + # openSUSE + sudo zypper install -y "$package" + elif command -v apk &> /dev/null; then + # Alpine + sudo apk add "$package" + else + echo "Error: No supported package manager found" >&2 + return 1 + fi +} + +# Service management +manage_service() { + local action=$1 + local service=$2 + + if command -v systemctl &> /dev/null; then + # systemd (most modern distros) + sudo systemctl "$action" "$service" + elif command -v service &> /dev/null; then + # SysV init + sudo service "$service" "$action" + else + echo "Error: No supported service manager found" >&2 + return 1 + fi +} +``` + +### GNU Coreutils (Linux Standard) + +```bash +# GNU-specific features +# These work on Linux but may not work on macOS/BSD + +# sed with -i (in-place editing) +sed -i 's/old/new/g' file.txt # Linux +sed -i '' 's/old/new/g' file.txt # macOS requires empty string + +# date with flexible parsing +date -d "yesterday" +%Y-%m-%d # Linux +date -v-1d +%Y-%m-%d # macOS + +# stat with -c format +stat -c "%s" file.txt # Linux (file size) +stat -f "%z" file.txt # macOS + +# readlink with -f (canonicalize) +readlink -f /path/to/file # Linux +# macOS doesn't have -f, use greadlink or: +python -c "import os; print(os.path.realpath('$file'))" + +# GNU find with -printf +find . -type f -printf "%p %s\n" # Linux +find . -type f -exec stat -f "%N %z" {} \; # macOS +``` + +--- + +## macOS Specifics + +### BSD vs GNU Commands + +```bash +# Detect and use GNU versions if available +setup_commands_macos() { + # Install GNU commands: brew install coreutils gnu-sed gnu-tar findutils + if command -v gsed &> /dev/null; then + SED=gsed + else + SED=sed + fi + + if command -v ggrep &> /dev/null; then + GREP=ggrep + else + GREP=grep + fi + + if command -v greadlink &> /dev/null; then + READLINK=greadlink + else + READLINK=readlink + fi + + if command -v gdate &> /dev/null; then + DATE=gdate + else + DATE=date + fi + + if command -v gstat &> /dev/null; then + STAT=gstat + else + STAT=stat + fi + + export SED GREP READLINK DATE STAT +} + +# Usage +setup_commands_macos +$SED -i 's/old/new/g' file.txt # Works on both platforms +``` + +### macOS-Specific Features + +```bash +# macOS filesystem (case-insensitive by default on APFS/HFS+) +check_case_sensitivity() { + touch /tmp/test_case + if [[ -f /tmp/TEST_CASE ]]; then + echo "Filesystem is case-insensitive" + else + echo "Filesystem is case-sensitive" + fi + rm -f /tmp/test_case /tmp/TEST_CASE +} + +# macOS extended attributes +# Set extended attribute +xattr -w com.example.myattr "value" file.txt + +# Get extended attribute +xattr -p com.example.myattr file.txt + +# List all extended attributes +xattr -l file.txt + +# Remove extended attribute +xattr -d com.example.myattr file.txt + +# macOS Spotlight +# Disable indexing for directory +mdutil -i off /path/to/directory + +# Search with mdfind (Spotlight from command line) +mdfind "kMDItemFSName == 'filename.txt'" + +# macOS clipboard +# Copy to clipboard +echo "text" | pbcopy + +# Paste from clipboard +pbpaste + +# macOS notifications +# Display notification +osascript -e 'display notification "Build complete" with title "Build Status"' + +# macOS open command +# Open file with default application +open file.pdf + +# Open URL +open https://example.com + +# Open current directory in Finder +open . +``` + +### Homebrew Package Management + +```bash +# Check if Homebrew is installed +if command -v brew &> /dev/null; then + # Install package + brew install package-name + + # Update Homebrew + brew update + + # Upgrade packages + brew upgrade + + # Search for package + brew search package-name + + # Get package info + brew info package-name +fi +``` + +--- + +## Windows (Git Bash) + +### Git Bash Environment + +```bash +# Git Bash uses MSYS2 runtime +# Provides Unix-like environment on Windows + +# Path handling +convert_path() { + local path=$1 + + if command -v cygpath &> /dev/null; then + # Convert Unix path to Windows + windows_path=$(cygpath -w "$path") + echo "$windows_path" + + # Convert Windows path to Unix + unix_path=$(cygpath -u "C:\\Users\\user\\file.txt") + echo "$unix_path" + else + # Manual conversion (Git Bash) + # /c/Users/user → C:\Users\user + echo "${path//\//\\}" | sed 's/^\\//' + fi +} + +# Git Bash path conventions +# C:\Users\user → /c/Users/user +# D:\data → /d/data + +# Home directory +echo "$HOME" # /c/Users/username +echo "$USERPROFILE" # Windows-style path + +# Temp directory +echo "$TEMP" # Windows temp +echo "$TMP" # Windows temp +echo "/tmp" # Git Bash temp (usually C:\Users\username\AppData\Local\Temp) +``` + +### Limited Features in Git Bash + +```bash +# Features NOT available in Git Bash: + +# 1. No systemd +# Use Windows services instead: +# sc query ServiceName +# net start ServiceName + +# 2. Limited signal support +# SIGTERM works, but some signals behave differently + +# 3. No /proc filesystem +# Use wmic or PowerShell: +# wmic process get processid,commandline + +# 4. Process handling differences +# ps command is available but limited +ps -W # Show Windows processes + +# 5. File permissions are simulated +# chmod works but doesn't map directly to Windows ACLs + +# 6. Symbolic links require administrator privileges +# Or Developer Mode enabled in Windows 10+ +``` + +### Windows-Specific Workarounds + +```bash +# Run PowerShell commands from Git Bash +run_powershell() { + local command=$1 + powershell.exe -Command "$command" +} + +# Example: Get Windows version +run_powershell "Get-ComputerInfo | Select-Object WindowsVersion" + +# Run cmd.exe commands +run_cmd() { + local command=$1 + cmd.exe /c "$command" +} + +# Example: Set Windows environment variable +run_cmd "setx MY_VAR value" + +# Check if running with admin privileges +is_admin() { + net session &> /dev/null + return $? +} + +if is_admin; then + echo "Running with administrator privileges" +else + echo "Not running as administrator" +fi + +# Windows line endings (CRLF vs LF) +fix_line_endings() { + local file=$1 + + # Convert CRLF to LF + dos2unix "$file" + + # Or with sed + sed -i 's/\r$//' "$file" + + # Convert LF to CRLF + unix2dos "$file" + + # Or with sed + sed -i 's/$/\r/' "$file" +} +``` + +### Git Bash Best Practices + +```bash +# Always handle spaces in Windows paths +process_file() { + local file="$1" # Always quote! + + # Windows paths often have spaces + # C:\Program Files\... +} + +# Use forward slashes when possible +cd /c/Program\ Files/Git # Works +cd "C:\Program Files\Git" # Also works, but... +cd C:\\Program\ Files\\Git # Avoid + +# Set Git config for line endings +git config --global core.autocrlf true # Windows +git config --global core.autocrlf input # Linux/macOS + +# Check Git Bash version +bash --version +uname -a # Shows MINGW or MSYS +``` + +--- + +## Windows (WSL) + +### WSL1 vs WSL2 + +```bash +# Detect WSL version +detect_wsl_version() { + if grep -qi microsoft /proc/version; then + if [[ $(uname -r) =~ microsoft ]]; then + echo "WSL 1" + elif [[ $(uname -r) =~ WSL2 ]]; then + echo "WSL 2" + else + # Check kernel version + if [[ $(uname -r) =~ ^4\. ]]; then + echo "WSL 1" + else + echo "WSL 2" + fi + fi + else + echo "Not WSL" + fi +} + +# WSL1 limitations: +# - No full syscall compatibility +# - File I/O slower on Windows filesystem +# - No Docker/containers (needs WSL2) + +# WSL2 improvements: +# - Full Linux kernel +# - Better filesystem performance +# - Docker/container support +# - Near-native Linux performance +``` + +### Windows Filesystem Access + +```bash +# Access Windows drives from WSL +# Mounted at /mnt/c, /mnt/d, etc. + +# List Windows drives +ls /mnt/ + +# Access Windows user directory +WINDOWS_HOME="/mnt/c/Users/$USER" +cd "$WINDOWS_HOME" + +# File permissions on Windows filesystem +# Files on /mnt/c are owned by root but accessible +# Permissions are simulated + +# Best practice: Use WSL filesystem for Linux files +# Use /home/username, not /mnt/c/... +# Much faster, especially in WSL1 +``` + +### WSL Interoperability + +```bash +# Run Windows executables from WSL +# .exe files are automatically executable + +# Run Windows commands +cmd.exe /c dir +notepad.exe file.txt +explorer.exe . # Open current directory in Windows Explorer + +# Run PowerShell +powershell.exe -Command "Get-Date" + +# Pipe between Linux and Windows +cat file.txt | clip.exe # Copy to Windows clipboard + +# Environment variables +# Windows environment is accessible with WSLENV + +# Share environment variable from Windows to WSL +# In PowerShell: +# $env:WSLENV = "MYVAR/p" +# This converts Windows paths to WSL paths +``` + +### WSL-Specific Configuration + +```bash +# /etc/wsl.conf configuration +cat > /etc/wsl.conf << 'EOF' +[automount] +enabled = true +root = /mnt/ +options = "metadata,umask=22,fmask=11" + +[network] +generateHosts = true +generateResolvConf = true + +[interop] +enabled = true +appendWindowsPath = true +EOF + +# Apply: wsl.exe --shutdown (from PowerShell) + +# Network differences +# WSL1: Shares network with Windows +# WSL2: NAT network, different IP + +# Get WSL IP address +ip addr show eth0 | grep -oP '(?<=inet\s)\d+(\.\d+){3}' + +# Access Windows services from WSL2 +# Use Windows IP, not localhost +# Or use: localhost (WSL2 has localhost forwarding) +``` + +--- + +## Container Environments + +### Docker Considerations + +```bash +# Minimal base images often lack bash +# alpine: Only has /bin/sh by default +# debian:slim: Has bash +# ubuntu: Has bash + +# Check if bash is available +if [ -f /bin/bash ]; then + exec /bin/bash "$@" +else + exec /bin/sh "$@" +fi + +# Container detection +is_docker() { + if [[ -f /.dockerenv ]] || grep -q docker /proc/1/cgroup 2>/dev/null; then + return 0 + else + return 1 + fi +} + +# PID 1 problem in containers +# Your script might be PID 1, which means: +# - Zombie process reaping is your responsibility +# - Signals behave differently + +# Solution: Use tini or dumb-init +# Or handle signals explicitly +handle_sigterm() { + # Forward to child processes + kill -TERM "$child_pid" 2>/dev/null + wait "$child_pid" + exit 0 +} + +trap handle_sigterm SIGTERM + +# Start main process +main_process & +child_pid=$! +wait "$child_pid" +``` + +### Kubernetes Considerations + +```bash +# Kubernetes-specific environment variables +if [[ -n "$KUBERNETES_SERVICE_HOST" ]]; then + echo "Running in Kubernetes" + + # Access Kubernetes API + KUBE_TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token) + KUBE_CA=/var/run/secrets/kubernetes.io/serviceaccount/ca.crt + + # Get pod name + POD_NAME=${POD_NAME:-$(hostname)} + + # Get namespace + NAMESPACE=$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace) +fi + +# Health checks +# Kubernetes expects: +# - HTTP probe on specific port +# - Or command that exits 0 for success + +# Liveness probe handler +handle_health_check() { + # Check if application is healthy + if check_health; then + exit 0 + else + exit 1 + fi +} + +# Readiness probe handler +handle_readiness_check() { + # Check if ready to serve traffic + if is_ready; then + exit 0 + else + exit 1 + fi +} + +# Graceful shutdown for rolling updates +# Kubernetes sends SIGTERM, waits (default 30s), then SIGKILL +trap 'graceful_shutdown' SIGTERM + +graceful_shutdown() { + echo "Received SIGTERM, shutting down gracefully..." + + # Stop accepting new connections + # Finish processing existing requests + # Close connections + # Exit + + exit 0 +} +``` + +### Container Best Practices + +```bash +# Don't assume specific users/groups exist +# Many containers run as non-root or random UID + +# Check current user +if [[ $EUID -eq 0 ]]; then + echo "Running as root" +else + echo "Running as user $EUID" +fi + +# Handle arbitrary UIDs (OpenShift) +# Files in mounted volumes may not be owned by container user +# Solution: Add current user to group, use group permissions + +# Minimal dependencies +# Container images should be small +# Don't install unnecessary packages + +# Use absolute paths or set PATH explicitly +export PATH=/usr/local/bin:/usr/bin:/bin + +# Environment variables for configuration +# Don't hardcode values, use env vars +DATABASE_URL=${DATABASE_URL:-postgres://localhost/db} + +# Logging to stdout/stderr +# Container orchestrators capture these +echo "Log message" # To stdout +echo "Error message" >&2 # To stderr + +# Don't write to filesystem (except for tmpfs) +# Containers are ephemeral +# Use volumes for persistent data +``` + +--- + +## Cross-Platform Patterns + +### Portable Command Wrapper + +```bash +# Create wrappers for platform-specific commands +setup_portable_commands() { + local os + os=$(detect_os) + + case "$os" in + linux) + SED=sed + READLINK="readlink -f" + DATE=date + STAT="stat -c" + GREP=grep + ;; + macos) + # Prefer GNU versions if available + SED=$(command -v gsed || echo sed) + READLINK=$(command -v greadlink || echo "echo") # No -f on BSD + DATE=$(command -v gdate || echo date) + STAT=$(command -v gstat || echo stat) + GREP=$(command -v ggrep || echo grep) + ;; + gitbash) + SED=sed + READLINK=readlink # Git Bash has GNU tools + DATE=date + STAT=stat + GREP=grep + ;; + esac + + export SED READLINK DATE STAT GREP +} + +# Use the wrappers +setup_portable_commands +$SED -i 's/old/new/g' file.txt +``` + +### Cross-Platform Temp Files + +```bash +# Portable temporary file creation +create_temp_file() { + # Works on all platforms + local temp_file + temp_file=$(mktemp) || { + # Fallback if mktemp doesn't exist + temp_file="/tmp/script.$$.$RANDOM" + touch "$temp_file" + } + + echo "$temp_file" +} + +# Portable temporary directory +create_temp_dir() { + local temp_dir + temp_dir=$(mktemp -d) || { + # Fallback + temp_dir="/tmp/script.$$.$RANDOM" + mkdir -p "$temp_dir" + } + + echo "$temp_dir" +} + +# Clean up temp files on exit +TEMP_DIR=$(create_temp_dir) +trap 'rm -rf "$TEMP_DIR"' EXIT +``` + +### Cross-Platform File Paths + +```bash +# Normalize paths across platforms +normalize_path() { + local path="$1" + + # Remove trailing slashes + path="${path%/}" + + # Convert backslashes to forward slashes (Windows) + path="${path//\\//}" + + # Resolve . and .. + # Use Python for reliable normalization + if command -v python3 &> /dev/null; then + path=$(python3 -c "import os; print(os.path.normpath('$path'))") + elif command -v python &> /dev/null; then + path=$(python -c "import os; print(os.path.normpath('$path'))") + fi + + echo "$path" +} + +# Get absolute path (cross-platform) +get_absolute_path() { + local path="$1" + + # Try readlink -f (Linux, Git Bash) + if readlink -f "$path" &> /dev/null; then + readlink -f "$path" + # Try realpath (most platforms) + elif command -v realpath &> /dev/null; then + realpath "$path" + # Fallback to Python + elif command -v python3 &> /dev/null; then + python3 -c "import os; print(os.path.abspath('$path'))" + # Fallback to cd + elif [[ -d "$path" ]]; then + (cd "$path" && pwd) + else + (cd "$(dirname "$path")" && echo "$(pwd)/$(basename "$path")") + fi +} +``` + +### Cross-Platform Process Management + +```bash +# Find process by name (cross-platform) +find_process() { + local process_name="$1" + + if command -v pgrep &> /dev/null; then + pgrep -f "$process_name" + else + ps aux | grep "$process_name" | grep -v grep | awk '{print $2}' + fi +} + +# Kill process by name (cross-platform) +kill_process() { + local process_name="$1" + + if command -v pkill &> /dev/null; then + pkill -f "$process_name" + else + local pids + pids=$(find_process "$process_name") + if [[ -n "$pids" ]]; then + kill $pids + fi + fi +} +``` + +--- + +## Command Compatibility Matrix + +| Command | Linux | macOS | Git Bash | Notes | +|---------|-------|-------|----------|-------| +| `sed -i` | ✓ | ✓* | ✓ | macOS needs `sed -i ''` | +| `date -d` | ✓ | ✗ | ✓ | macOS uses `-v` | +| `readlink -f` | ✓ | ✗ | ✓ | macOS needs `greadlink` | +| `stat -c` | ✓ | ✗ | ✓ | macOS uses `-f` | +| `grep -P` | ✓ | ✗ | ✓ | macOS doesn't support PCRE | +| `find -printf` | ✓ | ✗ | ✓ | macOS doesn't have `-printf` | +| `xargs -r` | ✓ | ✗ | ✓ | macOS doesn't have `-r` | +| `ps aux` | ✓ | ✓ | ✓* | Git Bash has limited output | +| `ls --color` | ✓ | ✗ | ✓ | macOS uses `-G` | +| `du -b` | ✓ | ✗ | ✓ | macOS doesn't support bytes | +| `mktemp` | ✓ | ✓ | ✓ | Works on all platforms | +| `timeout` | ✓ | ✗ | ✓ | macOS needs `gtimeout` | + +**Legend:** +- ✓ = Supported +- ✗ = Not supported +- ✓* = Supported with limitations + +--- + +## Testing Across Platforms + +```bash +# Test script on multiple platforms +test_platforms() { + local script="$1" + + echo "Testing on current platform: $(detect_os)" + bash -n "$script" || { + echo "Syntax error!" + return 1 + } + + # Run ShellCheck + if command -v shellcheck &> /dev/null; then + shellcheck "$script" || return 1 + fi + + # Run the script + bash "$script" || return 1 + + echo "Tests passed on $(detect_os)" +} + +# CI/CD matrix testing +# Use GitHub Actions, GitLab CI, etc. to test on multiple platforms +``` + +**Example GitHub Actions matrix:** +```yaml +jobs: + test: + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + steps: + - uses: actions/checkout@v3 + - name: Test script + run: bash test.sh +``` + +--- + +## Summary + +**Key takeaways for cross-platform bash scripts:** + +1. **Always detect the platform** before using platform-specific features +2. **Use portable commands** or provide fallbacks +3. **Test on all target platforms** (CI/CD with matrix builds) +4. **Avoid platform-specific assumptions** (file paths, users, services) +5. **Use ShellCheck** to catch portability issues +6. **Prefer POSIX compliance** when possible for maximum portability +7. **Document platform requirements** in script comments +8. **Provide GNU alternatives** on macOS when needed +9. **Handle path differences** carefully (especially Windows) +10. **Test in containers** if that's your deployment target + +For maximum portability: stick to POSIX shell (`#!/bin/sh`) and avoid bashisms unless you control the deployment environment. diff --git a/skills/bash-master/references/platform_specifics.md.backup b/skills/bash-master/references/platform_specifics.md.backup new file mode 100644 index 0000000..0f13cc8 --- /dev/null +++ b/skills/bash-master/references/platform_specifics.md.backup @@ -0,0 +1,1041 @@ +# Platform-Specific Bash Scripting + +Comprehensive guide to handling platform differences in bash scripts across Linux, macOS, Windows (Git Bash/WSL), and containers. + +--- + +## Table of Contents + +1. [Platform Detection](#platform-detection) +2. [Linux Specifics](#linux-specifics) +3. [macOS Specifics](#macos-specifics) +4. [Windows (Git Bash)](#windows-git-bash) +5. [Windows (WSL)](#windows-wsl) +6. [Container Environments](#container-environments) +7. [Cross-Platform Patterns](#cross-platform-patterns) +8. [Command Compatibility Matrix](#command-compatibility-matrix) + +--- + +## 🚨 CRITICAL GUIDELINES + +### Windows File Path Requirements + +**MANDATORY: Always Use Backslashes on Windows for File Paths** + +When using Edit or Write tools on Windows, you MUST use backslashes (`\`) in file paths, NOT forward slashes (`/`). + +**Examples:** +- ❌ WRONG: `D:/repos/project/file.tsx` +- ✅ CORRECT: `D:\repos\project\file.tsx` + +This applies to: +- Edit tool file_path parameter +- Write tool file_path parameter +- All file operations on Windows systems + + +### Documentation Guidelines + +**NEVER create new documentation files unless explicitly requested by the user.** + +- **Priority**: Update existing README.md files rather than creating new documentation +- **Repository cleanliness**: Keep repository root clean - only README.md unless user requests otherwise +- **Style**: Documentation should be concise, direct, and professional - avoid AI-generated tone +- **User preference**: Only create additional .md files when user specifically asks for documentation + + +--- + +## Platform Detection + +### Comprehensive Detection Script + +```bash +#!/usr/bin/env bash + +detect_os() { + case "$OSTYPE" in + linux-gnu*) + if grep -qi microsoft /proc/version 2>/dev/null; then + echo "wsl" + else + echo "linux" + fi + ;; + darwin*) + echo "macos" + ;; + msys*|mingw*|cygwin*) + echo "gitbash" + ;; + *) + echo "unknown" + ;; + esac +} + +detect_distro() { + # Only for Linux + if [[ -f /etc/os-release ]]; then + # shellcheck source=/dev/null + source /etc/os-release + echo "$ID" + elif [[ -f /etc/redhat-release ]]; then + echo "rhel" + elif [[ -f /etc/debian_version ]]; then + echo "debian" + else + echo "unknown" + fi +} + +detect_container() { + if [[ -f /.dockerenv ]]; then + echo "docker" + elif grep -q docker /proc/1/cgroup 2>/dev/null; then + echo "docker" + elif [[ -n "$KUBERNETES_SERVICE_HOST" ]]; then + echo "kubernetes" + else + echo "none" + fi +} + +# Usage +OS=$(detect_os) +DISTRO=$(detect_distro) +CONTAINER=$(detect_container) + +echo "OS: $OS" +echo "Distro: $DISTRO" +echo "Container: $CONTAINER" +``` + +### Environment Variables for Detection + +```bash +# Check various environment indicators +check_environment() { + echo "OSTYPE: $OSTYPE" + echo "MACHTYPE: $MACHTYPE" + echo "HOSTTYPE: $HOSTTYPE" + + # Kernel info + uname -s # Operating system name + uname -r # Kernel release + uname -m # Machine hardware + uname -p # Processor type + + # More detailed + uname -a # All information +} + +# Platform-specific variables +# Linux: OSTYPE=linux-gnu +# macOS: OSTYPE=darwin20.0 +# Git Bash: OSTYPE=msys +# Cygwin: OSTYPE=cygwin +# WSL: OSTYPE=linux-gnu (but with Microsoft in /proc/version) +``` + +--- + +## Linux Specifics + +### Linux-Only Features + +```bash +# /proc filesystem +get_process_info() { + local pid=$1 + + if [[ -d "/proc/$pid" ]]; then + echo "Command: $(cat /proc/$pid/cmdline | tr '\0' ' ')" + echo "Working dir: $(readlink /proc/$pid/cwd)" + echo "Executable: $(readlink /proc/$pid/exe)" + fi +} + +# systemd +check_systemd() { + if command -v systemctl &> /dev/null; then + systemctl status my-service + systemctl is-active my-service + systemctl is-enabled my-service + fi +} + +# cgroups +check_cgroups() { + if [[ -d /sys/fs/cgroup ]]; then + cat /sys/fs/cgroup/memory/memory.limit_in_bytes + fi +} + +# inotify for file watching +watch_directory() { + if command -v inotifywait &> /dev/null; then + inotifywait -m -r -e modify,create,delete /path/to/watch + fi +} +``` + +### Distribution-Specific Commands + +```bash +# Package management +install_package() { + local package=$1 + + if command -v apt-get &> /dev/null; then + # Debian/Ubuntu + sudo apt-get update + sudo apt-get install -y "$package" + elif command -v yum &> /dev/null; then + # RHEL/CentOS + sudo yum install -y "$package" + elif command -v dnf &> /dev/null; then + # Fedora + sudo dnf install -y "$package" + elif command -v pacman &> /dev/null; then + # Arch + sudo pacman -S --noconfirm "$package" + elif command -v zypper &> /dev/null; then + # openSUSE + sudo zypper install -y "$package" + elif command -v apk &> /dev/null; then + # Alpine + sudo apk add "$package" + else + echo "Error: No supported package manager found" >&2 + return 1 + fi +} + +# Service management +manage_service() { + local action=$1 + local service=$2 + + if command -v systemctl &> /dev/null; then + # systemd (most modern distros) + sudo systemctl "$action" "$service" + elif command -v service &> /dev/null; then + # SysV init + sudo service "$service" "$action" + else + echo "Error: No supported service manager found" >&2 + return 1 + fi +} +``` + +### GNU Coreutils (Linux Standard) + +```bash +# GNU-specific features +# These work on Linux but may not work on macOS/BSD + +# sed with -i (in-place editing) +sed -i 's/old/new/g' file.txt # Linux +sed -i '' 's/old/new/g' file.txt # macOS requires empty string + +# date with flexible parsing +date -d "yesterday" +%Y-%m-%d # Linux +date -v-1d +%Y-%m-%d # macOS + +# stat with -c format +stat -c "%s" file.txt # Linux (file size) +stat -f "%z" file.txt # macOS + +# readlink with -f (canonicalize) +readlink -f /path/to/file # Linux +# macOS doesn't have -f, use greadlink or: +python -c "import os; print(os.path.realpath('$file'))" + +# GNU find with -printf +find . -type f -printf "%p %s\n" # Linux +find . -type f -exec stat -f "%N %z" {} \; # macOS +``` + +--- + +## macOS Specifics + +### BSD vs GNU Commands + +```bash +# Detect and use GNU versions if available +setup_commands_macos() { + # Install GNU commands: brew install coreutils gnu-sed gnu-tar findutils + if command -v gsed &> /dev/null; then + SED=gsed + else + SED=sed + fi + + if command -v ggrep &> /dev/null; then + GREP=ggrep + else + GREP=grep + fi + + if command -v greadlink &> /dev/null; then + READLINK=greadlink + else + READLINK=readlink + fi + + if command -v gdate &> /dev/null; then + DATE=gdate + else + DATE=date + fi + + if command -v gstat &> /dev/null; then + STAT=gstat + else + STAT=stat + fi + + export SED GREP READLINK DATE STAT +} + +# Usage +setup_commands_macos +$SED -i 's/old/new/g' file.txt # Works on both platforms +``` + +### macOS-Specific Features + +```bash +# macOS filesystem (case-insensitive by default on APFS/HFS+) +check_case_sensitivity() { + touch /tmp/test_case + if [[ -f /tmp/TEST_CASE ]]; then + echo "Filesystem is case-insensitive" + else + echo "Filesystem is case-sensitive" + fi + rm -f /tmp/test_case /tmp/TEST_CASE +} + +# macOS extended attributes +# Set extended attribute +xattr -w com.example.myattr "value" file.txt + +# Get extended attribute +xattr -p com.example.myattr file.txt + +# List all extended attributes +xattr -l file.txt + +# Remove extended attribute +xattr -d com.example.myattr file.txt + +# macOS Spotlight +# Disable indexing for directory +mdutil -i off /path/to/directory + +# Search with mdfind (Spotlight from command line) +mdfind "kMDItemFSName == 'filename.txt'" + +# macOS clipboard +# Copy to clipboard +echo "text" | pbcopy + +# Paste from clipboard +pbpaste + +# macOS notifications +# Display notification +osascript -e 'display notification "Build complete" with title "Build Status"' + +# macOS open command +# Open file with default application +open file.pdf + +# Open URL +open https://example.com + +# Open current directory in Finder +open . +``` + +### Homebrew Package Management + +```bash +# Check if Homebrew is installed +if command -v brew &> /dev/null; then + # Install package + brew install package-name + + # Update Homebrew + brew update + + # Upgrade packages + brew upgrade + + # Search for package + brew search package-name + + # Get package info + brew info package-name +fi +``` + +--- + +## Windows (Git Bash) + +### Git Bash Environment + +```bash +# Git Bash uses MSYS2 runtime +# Provides Unix-like environment on Windows + +# Path handling +convert_path() { + local path=$1 + + if command -v cygpath &> /dev/null; then + # Convert Unix path to Windows + windows_path=$(cygpath -w "$path") + echo "$windows_path" + + # Convert Windows path to Unix + unix_path=$(cygpath -u "C:\\Users\\user\\file.txt") + echo "$unix_path" + else + # Manual conversion (Git Bash) + # /c/Users/user → C:\Users\user + echo "${path//\//\\}" | sed 's/^\\//' + fi +} + +# Git Bash path conventions +# C:\Users\user → /c/Users/user +# D:\data → /d/data + +# Home directory +echo "$HOME" # /c/Users/username +echo "$USERPROFILE" # Windows-style path + +# Temp directory +echo "$TEMP" # Windows temp +echo "$TMP" # Windows temp +echo "/tmp" # Git Bash temp (usually C:\Users\username\AppData\Local\Temp) +``` + +### Limited Features in Git Bash + +```bash +# Features NOT available in Git Bash: + +# 1. No systemd +# Use Windows services instead: +# sc query ServiceName +# net start ServiceName + +# 2. Limited signal support +# SIGTERM works, but some signals behave differently + +# 3. No /proc filesystem +# Use wmic or PowerShell: +# wmic process get processid,commandline + +# 4. Process handling differences +# ps command is available but limited +ps -W # Show Windows processes + +# 5. File permissions are simulated +# chmod works but doesn't map directly to Windows ACLs + +# 6. Symbolic links require administrator privileges +# Or Developer Mode enabled in Windows 10+ +``` + +### Windows-Specific Workarounds + +```bash +# Run PowerShell commands from Git Bash +run_powershell() { + local command=$1 + powershell.exe -Command "$command" +} + +# Example: Get Windows version +run_powershell "Get-ComputerInfo | Select-Object WindowsVersion" + +# Run cmd.exe commands +run_cmd() { + local command=$1 + cmd.exe /c "$command" +} + +# Example: Set Windows environment variable +run_cmd "setx MY_VAR value" + +# Check if running with admin privileges +is_admin() { + net session &> /dev/null + return $? +} + +if is_admin; then + echo "Running with administrator privileges" +else + echo "Not running as administrator" +fi + +# Windows line endings (CRLF vs LF) +fix_line_endings() { + local file=$1 + + # Convert CRLF to LF + dos2unix "$file" + + # Or with sed + sed -i 's/\r$//' "$file" + + # Convert LF to CRLF + unix2dos "$file" + + # Or with sed + sed -i 's/$/\r/' "$file" +} +``` + +### Git Bash Best Practices + +```bash +# Always handle spaces in Windows paths +process_file() { + local file="$1" # Always quote! + + # Windows paths often have spaces + # C:\Program Files\... +} + +# Use forward slashes when possible +cd /c/Program\ Files/Git # Works +cd "C:\Program Files\Git" # Also works, but... +cd C:\\Program\ Files\\Git # Avoid + +# Set Git config for line endings +git config --global core.autocrlf true # Windows +git config --global core.autocrlf input # Linux/macOS + +# Check Git Bash version +bash --version +uname -a # Shows MINGW or MSYS +``` + +--- + +## Windows (WSL) + +### WSL1 vs WSL2 + +```bash +# Detect WSL version +detect_wsl_version() { + if grep -qi microsoft /proc/version; then + if [[ $(uname -r) =~ microsoft ]]; then + echo "WSL 1" + elif [[ $(uname -r) =~ WSL2 ]]; then + echo "WSL 2" + else + # Check kernel version + if [[ $(uname -r) =~ ^4\. ]]; then + echo "WSL 1" + else + echo "WSL 2" + fi + fi + else + echo "Not WSL" + fi +} + +# WSL1 limitations: +# - No full syscall compatibility +# - File I/O slower on Windows filesystem +# - No Docker/containers (needs WSL2) + +# WSL2 improvements: +# - Full Linux kernel +# - Better filesystem performance +# - Docker/container support +# - Near-native Linux performance +``` + +### Windows Filesystem Access + +```bash +# Access Windows drives from WSL +# Mounted at /mnt/c, /mnt/d, etc. + +# List Windows drives +ls /mnt/ + +# Access Windows user directory +WINDOWS_HOME="/mnt/c/Users/$USER" +cd "$WINDOWS_HOME" + +# File permissions on Windows filesystem +# Files on /mnt/c are owned by root but accessible +# Permissions are simulated + +# Best practice: Use WSL filesystem for Linux files +# Use /home/username, not /mnt/c/... +# Much faster, especially in WSL1 +``` + +### WSL Interoperability + +```bash +# Run Windows executables from WSL +# .exe files are automatically executable + +# Run Windows commands +cmd.exe /c dir +notepad.exe file.txt +explorer.exe . # Open current directory in Windows Explorer + +# Run PowerShell +powershell.exe -Command "Get-Date" + +# Pipe between Linux and Windows +cat file.txt | clip.exe # Copy to Windows clipboard + +# Environment variables +# Windows environment is accessible with WSLENV + +# Share environment variable from Windows to WSL +# In PowerShell: +# $env:WSLENV = "MYVAR/p" +# This converts Windows paths to WSL paths +``` + +### WSL-Specific Configuration + +```bash +# /etc/wsl.conf configuration +cat > /etc/wsl.conf << 'EOF' +[automount] +enabled = true +root = /mnt/ +options = "metadata,umask=22,fmask=11" + +[network] +generateHosts = true +generateResolvConf = true + +[interop] +enabled = true +appendWindowsPath = true +EOF + +# Apply: wsl.exe --shutdown (from PowerShell) + +# Network differences +# WSL1: Shares network with Windows +# WSL2: NAT network, different IP + +# Get WSL IP address +ip addr show eth0 | grep -oP '(?<=inet\s)\d+(\.\d+){3}' + +# Access Windows services from WSL2 +# Use Windows IP, not localhost +# Or use: localhost (WSL2 has localhost forwarding) +``` + +--- + +## Container Environments + +### Docker Considerations + +```bash +# Minimal base images often lack bash +# alpine: Only has /bin/sh by default +# debian:slim: Has bash +# ubuntu: Has bash + +# Check if bash is available +if [ -f /bin/bash ]; then + exec /bin/bash "$@" +else + exec /bin/sh "$@" +fi + +# Container detection +is_docker() { + if [[ -f /.dockerenv ]] || grep -q docker /proc/1/cgroup 2>/dev/null; then + return 0 + else + return 1 + fi +} + +# PID 1 problem in containers +# Your script might be PID 1, which means: +# - Zombie process reaping is your responsibility +# - Signals behave differently + +# Solution: Use tini or dumb-init +# Or handle signals explicitly +handle_sigterm() { + # Forward to child processes + kill -TERM "$child_pid" 2>/dev/null + wait "$child_pid" + exit 0 +} + +trap handle_sigterm SIGTERM + +# Start main process +main_process & +child_pid=$! +wait "$child_pid" +``` + +### Kubernetes Considerations + +```bash +# Kubernetes-specific environment variables +if [[ -n "$KUBERNETES_SERVICE_HOST" ]]; then + echo "Running in Kubernetes" + + # Access Kubernetes API + KUBE_TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token) + KUBE_CA=/var/run/secrets/kubernetes.io/serviceaccount/ca.crt + + # Get pod name + POD_NAME=${POD_NAME:-$(hostname)} + + # Get namespace + NAMESPACE=$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace) +fi + +# Health checks +# Kubernetes expects: +# - HTTP probe on specific port +# - Or command that exits 0 for success + +# Liveness probe handler +handle_health_check() { + # Check if application is healthy + if check_health; then + exit 0 + else + exit 1 + fi +} + +# Readiness probe handler +handle_readiness_check() { + # Check if ready to serve traffic + if is_ready; then + exit 0 + else + exit 1 + fi +} + +# Graceful shutdown for rolling updates +# Kubernetes sends SIGTERM, waits (default 30s), then SIGKILL +trap 'graceful_shutdown' SIGTERM + +graceful_shutdown() { + echo "Received SIGTERM, shutting down gracefully..." + + # Stop accepting new connections + # Finish processing existing requests + # Close connections + # Exit + + exit 0 +} +``` + +### Container Best Practices + +```bash +# Don't assume specific users/groups exist +# Many containers run as non-root or random UID + +# Check current user +if [[ $EUID -eq 0 ]]; then + echo "Running as root" +else + echo "Running as user $EUID" +fi + +# Handle arbitrary UIDs (OpenShift) +# Files in mounted volumes may not be owned by container user +# Solution: Add current user to group, use group permissions + +# Minimal dependencies +# Container images should be small +# Don't install unnecessary packages + +# Use absolute paths or set PATH explicitly +export PATH=/usr/local/bin:/usr/bin:/bin + +# Environment variables for configuration +# Don't hardcode values, use env vars +DATABASE_URL=${DATABASE_URL:-postgres://localhost/db} + +# Logging to stdout/stderr +# Container orchestrators capture these +echo "Log message" # To stdout +echo "Error message" >&2 # To stderr + +# Don't write to filesystem (except for tmpfs) +# Containers are ephemeral +# Use volumes for persistent data +``` + +--- + +## Cross-Platform Patterns + +### Portable Command Wrapper + +```bash +# Create wrappers for platform-specific commands +setup_portable_commands() { + local os + os=$(detect_os) + + case "$os" in + linux) + SED=sed + READLINK="readlink -f" + DATE=date + STAT="stat -c" + GREP=grep + ;; + macos) + # Prefer GNU versions if available + SED=$(command -v gsed || echo sed) + READLINK=$(command -v greadlink || echo "echo") # No -f on BSD + DATE=$(command -v gdate || echo date) + STAT=$(command -v gstat || echo stat) + GREP=$(command -v ggrep || echo grep) + ;; + gitbash) + SED=sed + READLINK=readlink # Git Bash has GNU tools + DATE=date + STAT=stat + GREP=grep + ;; + esac + + export SED READLINK DATE STAT GREP +} + +# Use the wrappers +setup_portable_commands +$SED -i 's/old/new/g' file.txt +``` + +### Cross-Platform Temp Files + +```bash +# Portable temporary file creation +create_temp_file() { + # Works on all platforms + local temp_file + temp_file=$(mktemp) || { + # Fallback if mktemp doesn't exist + temp_file="/tmp/script.$$.$RANDOM" + touch "$temp_file" + } + + echo "$temp_file" +} + +# Portable temporary directory +create_temp_dir() { + local temp_dir + temp_dir=$(mktemp -d) || { + # Fallback + temp_dir="/tmp/script.$$.$RANDOM" + mkdir -p "$temp_dir" + } + + echo "$temp_dir" +} + +# Clean up temp files on exit +TEMP_DIR=$(create_temp_dir) +trap 'rm -rf "$TEMP_DIR"' EXIT +``` + +### Cross-Platform File Paths + +```bash +# Normalize paths across platforms +normalize_path() { + local path="$1" + + # Remove trailing slashes + path="${path%/}" + + # Convert backslashes to forward slashes (Windows) + path="${path//\\//}" + + # Resolve . and .. + # Use Python for reliable normalization + if command -v python3 &> /dev/null; then + path=$(python3 -c "import os; print(os.path.normpath('$path'))") + elif command -v python &> /dev/null; then + path=$(python -c "import os; print(os.path.normpath('$path'))") + fi + + echo "$path" +} + +# Get absolute path (cross-platform) +get_absolute_path() { + local path="$1" + + # Try readlink -f (Linux, Git Bash) + if readlink -f "$path" &> /dev/null; then + readlink -f "$path" + # Try realpath (most platforms) + elif command -v realpath &> /dev/null; then + realpath "$path" + # Fallback to Python + elif command -v python3 &> /dev/null; then + python3 -c "import os; print(os.path.abspath('$path'))" + # Fallback to cd + elif [[ -d "$path" ]]; then + (cd "$path" && pwd) + else + (cd "$(dirname "$path")" && echo "$(pwd)/$(basename "$path")") + fi +} +``` + +### Cross-Platform Process Management + +```bash +# Find process by name (cross-platform) +find_process() { + local process_name="$1" + + if command -v pgrep &> /dev/null; then + pgrep -f "$process_name" + else + ps aux | grep "$process_name" | grep -v grep | awk '{print $2}' + fi +} + +# Kill process by name (cross-platform) +kill_process() { + local process_name="$1" + + if command -v pkill &> /dev/null; then + pkill -f "$process_name" + else + local pids + pids=$(find_process "$process_name") + if [[ -n "$pids" ]]; then + kill $pids + fi + fi +} +``` + +--- + +## Command Compatibility Matrix + +| Command | Linux | macOS | Git Bash | Notes | +|---------|-------|-------|----------|-------| +| `sed -i` | ✓ | ✓* | ✓ | macOS needs `sed -i ''` | +| `date -d` | ✓ | ✗ | ✓ | macOS uses `-v` | +| `readlink -f` | ✓ | ✗ | ✓ | macOS needs `greadlink` | +| `stat -c` | ✓ | ✗ | ✓ | macOS uses `-f` | +| `grep -P` | ✓ | ✗ | ✓ | macOS doesn't support PCRE | +| `find -printf` | ✓ | ✗ | ✓ | macOS doesn't have `-printf` | +| `xargs -r` | ✓ | ✗ | ✓ | macOS doesn't have `-r` | +| `ps aux` | ✓ | ✓ | ✓* | Git Bash has limited output | +| `ls --color` | ✓ | ✗ | ✓ | macOS uses `-G` | +| `du -b` | ✓ | ✗ | ✓ | macOS doesn't support bytes | +| `mktemp` | ✓ | ✓ | ✓ | Works on all platforms | +| `timeout` | ✓ | ✗ | ✓ | macOS needs `gtimeout` | + +**Legend:** +- ✓ = Supported +- ✗ = Not supported +- ✓* = Supported with limitations + +--- + +## Testing Across Platforms + +```bash +# Test script on multiple platforms +test_platforms() { + local script="$1" + + echo "Testing on current platform: $(detect_os)" + bash -n "$script" || { + echo "Syntax error!" + return 1 + } + + # Run ShellCheck + if command -v shellcheck &> /dev/null; then + shellcheck "$script" || return 1 + fi + + # Run the script + bash "$script" || return 1 + + echo "Tests passed on $(detect_os)" +} + +# CI/CD matrix testing +# Use GitHub Actions, GitLab CI, etc. to test on multiple platforms +``` + +**Example GitHub Actions matrix:** +```yaml +jobs: + test: + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + steps: + - uses: actions/checkout@v3 + - name: Test script + run: bash test.sh +``` + +--- + +## Summary + +**Key takeaways for cross-platform bash scripts:** + +1. **Always detect the platform** before using platform-specific features +2. **Use portable commands** or provide fallbacks +3. **Test on all target platforms** (CI/CD with matrix builds) +4. **Avoid platform-specific assumptions** (file paths, users, services) +5. **Use ShellCheck** to catch portability issues +6. **Prefer POSIX compliance** when possible for maximum portability +7. **Document platform requirements** in script comments +8. **Provide GNU alternatives** on macOS when needed +9. **Handle path differences** carefully (especially Windows) +10. **Test in containers** if that's your deployment target + +For maximum portability: stick to POSIX shell (`#!/bin/sh`) and avoid bashisms unless you control the deployment environment. diff --git a/skills/bash-master/references/resources.md b/skills/bash-master/references/resources.md new file mode 100644 index 0000000..63a94f2 --- /dev/null +++ b/skills/bash-master/references/resources.md @@ -0,0 +1,739 @@ +# Bash Scripting Resources + +Comprehensive directory of authoritative sources, tools, and learning resources for bash scripting. + +--- + +## Table of Contents + +1. [Official Documentation](#official-documentation) +2. [Style Guides and Standards](#style-guides-and-standards) +3. [Tools and Utilities](#tools-and-utilities) +4. [Learning Resources](#learning-resources) +5. [Community Resources](#community-resources) +6. [Books](#books) +7. [Cheat Sheets and Quick References](#cheat-sheets-and-quick-references) +8. [Testing and Quality](#testing-and-quality) +9. [Platform-Specific Resources](#platform-specific-resources) +10. [Advanced Topics](#advanced-topics) + +--- + +## 🚨 CRITICAL GUIDELINES + +### Windows File Path Requirements + +**MANDATORY: Always Use Backslashes on Windows for File Paths** + +When using Edit or Write tools on Windows, you MUST use backslashes (`\`) in file paths, NOT forward slashes (`/`). + +**Examples:** +- ❌ WRONG: `D:/repos/project/file.tsx` +- ✅ CORRECT: `D:\repos\project\file.tsx` + +This applies to: +- Edit tool file_path parameter +- Write tool file_path parameter +- All file operations on Windows systems + + +### Documentation Guidelines + +**NEVER create new documentation files unless explicitly requested by the user.** + +- **Priority**: Update existing README.md files rather than creating new documentation +- **Repository cleanliness**: Keep repository root clean - only README.md unless user requests otherwise +- **Style**: Documentation should be concise, direct, and professional - avoid AI-generated tone +- **User preference**: Only create additional .md files when user specifically asks for documentation + + +--- + +## Official Documentation + +### Bash Manual + +**GNU Bash Reference Manual** +- **URL:** https://www.gnu.org/software/bash/manual/ +- **Description:** The authoritative reference for bash features, syntax, and built-ins +- **Use for:** Detailed feature documentation, syntax clarification, version-specific features + +**Bash Man Page** +```bash +man bash # Complete bash documentation +man bash-builtins # Built-in commands +``` +- **Use for:** Quick reference on local system, offline documentation + +### POSIX Standards + +**POSIX Shell Command Language** +- **URL:** https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html +- **Description:** IEEE/Open Group specification for portable shell scripting +- **Use for:** Writing portable scripts, understanding sh vs bash differences + +**POSIX Utilities** +- **URL:** https://pubs.opengroup.org/onlinepubs/9699919799/idx/utilities.html +- **Description:** Standard utilities available in POSIX-compliant systems +- **Use for:** Portable command usage, cross-platform compatibility + +### Command Documentation + +**GNU Coreutils Manual** +- **URL:** https://www.gnu.org/software/coreutils/manual/ +- **Description:** Documentation for core GNU utilities (ls, cat, grep, etc.) +- **Use for:** Understanding Linux command behavior, GNU-specific features + +**Man Pages Online** +- **URL:** https://man7.org/linux/man-pages/ +- **URL:** https://www.freebsd.org/cgi/man.cgi (BSD/macOS) +- **Description:** Online searchable man pages +- **Use for:** Quick online reference, comparing Linux vs BSD commands + +--- + +## Style Guides and Standards + +### Google Shell Style Guide + +**URL:** https://google.github.io/styleguide/shellguide.html + +**Key Points:** +- Industry-standard practices from Google +- Covers naming conventions, formatting, best practices +- When to use shell vs other languages +- Safety and portability guidelines + +**Use for:** Professional code style, team standards, code reviews + +### Defensive Bash Programming + +**URL:** https://kfirlavi.herokuapp.com/blog/2012/11/14/defensive-bash-programming + +**Key Points:** +- Writing robust bash scripts +- Error handling patterns +- Safe coding practices +- Code organization + +**Use for:** Improving script reliability, avoiding common pitfalls + +### Shell Style Guide (GitHub) + +**URL:** https://github.com/bahamas10/bash-style-guide + +**Key Points:** +- Community-driven style guidelines +- Practical examples +- Modern bash features + +**Use for:** Alternative perspectives on style, community standards + +--- + +## Tools and Utilities + +### ShellCheck + +**Website:** https://www.shellcheck.net/ +**GitHub:** https://github.com/koalaman/shellcheck +**Online Tool:** https://www.shellcheck.net/ (paste code for instant feedback) + +**Description:** Static analysis tool for shell scripts + +**Installation:** +```bash +# Ubuntu/Debian +apt-get install shellcheck + +# macOS +brew install shellcheck + +# Windows (Scoop) +scoop install shellcheck + +# Via Docker +docker run --rm -v "$PWD:/mnt" koalaman/shellcheck script.sh +``` + +**Usage:** +```bash +shellcheck script.sh # Check script +shellcheck -x script.sh # Follow source statements +shellcheck -f json script.sh # JSON output +shellcheck -e SC2086 script.sh # Exclude specific warnings +``` + +**ShellCheck Wiki:** https://www.shellcheck.net/wiki/ +- Detailed explanations of every warning +- **Use for:** Understanding and fixing ShellCheck warnings + +### shfmt + +**GitHub:** https://github.com/mvdan/sh + +**Description:** Shell script formatter + +**Installation:** +```bash +# macOS +brew install shfmt + +# Go +go install mvdan.cc/sh/v3/cmd/shfmt@latest +``` + +**Usage:** +```bash +shfmt -i 4 -w script.sh # Format with 4-space indent +shfmt -d script.sh # Show diff without modifying +shfmt -l script.sh # List files that would be changed +``` + +**Use for:** Consistent code formatting, automated formatting in CI + +### BATS (Bash Automated Testing System) + +**GitHub:** https://github.com/bats-core/bats-core + +**Description:** Testing framework for bash scripts + +**Installation:** +```bash +git clone https://github.com/bats-core/bats-core.git +cd bats-core +./install.sh /usr/local +``` + +**Usage:** +```bash +bats test/ # Run all tests +bats test/script.bats # Run specific test file +bats --tap test/ # TAP output format +``` + +**Documentation:** https://bats-core.readthedocs.io/ + +**Use for:** Unit testing bash scripts, CI/CD integration + +### bashate + +**GitHub:** https://github.com/openstack/bashate + +**Description:** Style checker (used by OpenStack) + +**Installation:** +```bash +pip install bashate +``` + +**Usage:** +```bash +bashate script.sh +bashate -i E006 script.sh # Ignore specific errors +``` + +**Use for:** Additional style checking beyond ShellCheck + +### checkbashisms + +**Package:** devscripts (Debian) + +**Description:** Checks for bashisms in sh scripts + +**Installation:** +```bash +apt-get install devscripts # Ubuntu/Debian +``` + +**Usage:** +```bash +checkbashisms script.sh +checkbashisms -f script.sh # Force check even if #!/bin/bash +``` + +**Use for:** Ensuring POSIX compliance, portable scripts + +--- + +## Learning Resources + +### Interactive Tutorials + +**Bash Academy** +- **URL:** https://www.bash.academy/ +- **Description:** Modern, comprehensive bash tutorial +- **Topics:** Basics, scripting, advanced features +- **Use for:** Learning bash from scratch, structured learning path + +**Learn Shell** +- **URL:** https://www.learnshell.org/ +- **Description:** Interactive bash tutorial with exercises +- **Use for:** Hands-on practice, beginners + +**Bash Scripting Tutorial** +- **URL:** https://linuxconfig.org/bash-scripting-tutorial +- **Description:** Comprehensive tutorial series +- **Use for:** Step-by-step learning, examples + +### Guides and Documentation + +**Bash Guide for Beginners** +- **URL:** https://tldp.org/LDP/Bash-Beginners-Guide/html/ +- **Author:** The Linux Documentation Project +- **Description:** Comprehensive guide covering basics to intermediate +- **Use for:** Structured learning, reference material + +**Advanced Bash-Scripting Guide** +- **URL:** https://tldp.org/LDP/abs/html/ +- **Description:** In-depth coverage of advanced bash topics +- **Topics:** Complex scripting, text processing, system administration +- **Use for:** Advanced techniques, real-world examples + +**Bash Hackers Wiki** +- **URL:** https://wiki.bash-hackers.org/ +- **Alternative:** https://flokoe.github.io/bash-hackers-wiki/ (maintained mirror) +- **Description:** Community-driven bash documentation +- **Use for:** In-depth explanations, advanced topics, edge cases + +**Greg's Wiki (Wooledge)** +- **URL:** https://mywiki.wooledge.org/ +- **Key Pages:** + - https://mywiki.wooledge.org/BashFAQ + - https://mywiki.wooledge.org/BashPitfalls + - https://mywiki.wooledge.org/BashGuide +- **Description:** High-quality bash Q&A and guides +- **Use for:** Common questions, avoiding pitfalls, best practices + +### Video Courses + +**Bash Scripting on Linux (Udemy)** +- **Description:** Comprehensive video course +- **Use for:** Visual learners + +**Shell Scripting: Discover How to Automate Command Line Tasks (Udemy)** +- **Description:** Practical shell scripting course +- **Use for:** Automation-focused learning + +**LinkedIn Learning - Learning Bash Scripting** +- **Description:** Professional development course +- **Use for:** Structured corporate training + +--- + +## Community Resources + +### Stack Overflow + +**Bash Tag** +- **URL:** https://stackoverflow.com/questions/tagged/bash +- **Use for:** Specific problems, code review, troubleshooting + +**Top Questions:** +- **URL:** https://stackoverflow.com/questions/tagged/bash?tab=Votes +- **Use for:** Common problems and solutions + +### Unix & Linux Stack Exchange + +**URL:** https://unix.stackexchange.com/ + +**Shell Tag:** https://unix.stackexchange.com/questions/tagged/shell +**Bash Tag:** https://unix.stackexchange.com/questions/tagged/bash + +**Use for:** Unix/Linux-specific questions, system administration + +### Reddit + +**/r/bash** +- **URL:** https://www.reddit.com/r/bash/ +- **Description:** Bash scripting community +- **Use for:** Discussions, learning resources, help + +**/r/commandline** +- **URL:** https://www.reddit.com/r/commandline/ +- **Description:** Command-line interface community +- **Use for:** CLI tips, tools, productivity + +### IRC/Chat + +**Freenode #bash** +- **URL:** irc://irc.freenode.net/bash +- **Description:** Real-time bash help channel +- **Use for:** Live help, quick questions + +**Libera.Chat #bash** +- **URL:** irc://irc.libera.chat/bash +- **Description:** Alternative IRC channel +- **Use for:** Live community support + +--- + +## Books + +### "Classic Shell Scripting" by Arnold Robbins & Nelson Beebe + +**Publisher:** O'Reilly +**ISBN:** 978-0596005955 + +**Topics:** +- Shell basics and portability +- Text processing and filters +- Shell programming patterns + +**Use for:** Comprehensive reference, professional development + +### "Learning the bash Shell" by Cameron Newham + +**Publisher:** O'Reilly +**ISBN:** 978-0596009656 + +**Topics:** +- Bash basics +- Command-line editing +- Shell programming + +**Use for:** Systematic learning, reference + +### "Bash Cookbook" by Carl Albing & JP Vossen + +**Publisher:** O'Reilly +**ISBN:** 978-1491975336 + +**Topics:** +- Solutions to common problems +- Recipes and patterns +- Real-world examples + +**Use for:** Problem-solving, practical examples + +### "Wicked Cool Shell Scripts" by Dave Taylor & Brandon Perry + +**Publisher:** No Starch Press +**ISBN:** 978-1593276027 + +**Topics:** +- Creative shell scripting +- System administration +- Fun and practical scripts + +**Use for:** Inspiration, practical applications + +### "The Linux Command Line" by William Shotts + +**Publisher:** No Starch Press +**ISBN:** 978-1593279523 +**Free PDF:** https://linuxcommand.org/tlcl.php + +**Topics:** +- Command-line basics +- Shell scripting fundamentals +- Linux system administration + +**Use for:** Beginners, comprehensive introduction + +--- + +## Cheat Sheets and Quick References + +### Bash Cheat Sheet (DevHints) + +**URL:** https://devhints.io/bash + +**Content:** +- Quick syntax reference +- Common patterns +- Parameter expansion +- Conditionals and loops + +**Use for:** Quick lookups, syntax reminders + +### Bash Scripting Cheat Sheet (GitHub) + +**URL:** https://github.com/LeCoupa/awesome-cheatsheets/blob/master/languages/bash.sh + +**Content:** +- Comprehensive syntax guide +- Examples and explanations +- Best practices + +**Use for:** Single-file reference + +### explainshell.com + +**URL:** https://explainshell.com/ + +**Description:** Interactive tool that explains shell commands + +**Example:** Paste `tar -xzvf file.tar.gz` to get detailed explanation of each flag + +**Use for:** Understanding complex commands, learning command options + +### Command Line Fu + +**URL:** https://www.commandlinefu.com/ + +**Description:** Community-contributed command-line snippets + +**Use for:** One-liners, clever solutions, learning new commands + +### tldr Pages + +**URL:** https://tldr.sh/ +**GitHub:** https://github.com/tldr-pages/tldr + +**Description:** Simplified man pages with examples + +**Installation:** +```bash +npm install -g tldr +# Or +brew install tldr +``` + +**Usage:** +```bash +tldr tar +tldr grep +tldr find +``` + +**Use for:** Quick command examples, practical usage + +--- + +## Testing and Quality + +### Testing Frameworks + +**BATS (Bash Automated Testing System)** +- **URL:** https://github.com/bats-core/bats-core +- **Documentation:** https://bats-core.readthedocs.io/ +- **Use for:** Unit testing + +**shUnit2** +- **URL:** https://github.com/kward/shunit2 +- **Description:** xUnit-based unit testing framework +- **Use for:** Alternative to BATS + +**Bash Unit** +- **URL:** https://github.com/pgrange/bash_unit +- **Description:** Bash unit testing +- **Use for:** Lightweight testing + +### CI/CD Integration + +**GitHub Actions Example** +```yaml +name: Test +on: [push, pull_request] +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Install ShellCheck + run: sudo apt-get install -y shellcheck + - name: Run ShellCheck + run: find . -name "*.sh" -exec shellcheck {} + + - name: Install BATS + run: | + git clone https://github.com/bats-core/bats-core.git + cd bats-core + sudo ./install.sh /usr/local + - name: Run Tests + run: bats test/ +``` + +**GitLab CI Example** +```yaml +test: + image: koalaman/shellcheck-alpine + script: + - find . -name "*.sh" -exec shellcheck {} + + +bats: + image: bats/bats + script: + - bats test/ +``` + +### Code Coverage + +**bashcov** +- **URL:** https://github.com/infertux/bashcov +- **Description:** Code coverage for bash +- **Installation:** `gem install bashcov` +- **Use for:** Measuring test coverage + +--- + +## Platform-Specific Resources + +### Linux + +**Linux Man Pages** +- **URL:** https://man7.org/linux/man-pages/ +- **Use for:** Linux-specific command documentation + +**systemd Documentation** +- **URL:** https://www.freedesktop.org/software/systemd/man/ +- **Use for:** systemd service management + +### macOS + +**macOS Man Pages** +- **URL:** https://www.freebsd.org/cgi/man.cgi +- **Description:** BSD-based commands (similar to macOS) +- **Use for:** macOS command differences + +**Homebrew** +- **URL:** https://brew.sh/ +- **Use for:** Installing GNU tools on macOS + +### Windows + +**Git for Windows** +- **URL:** https://gitforwindows.org/ +- **Documentation:** https://github.com/git-for-windows/git/wiki +- **Use for:** Git Bash on Windows + +**WSL Documentation** +- **URL:** https://docs.microsoft.com/en-us/windows/wsl/ +- **Use for:** Windows Subsystem for Linux + +**Cygwin** +- **URL:** https://www.cygwin.com/ +- **Use for:** POSIX environment on Windows + +### Containers + +**Docker Bash Best Practices** +- **URL:** https://docs.docker.com/develop/develop-images/dockerfile_best-practices/ +- **Use for:** Bash in containers + +**Container Best Practices** +- **URL:** https://cloud.google.com/architecture/best-practices-for-building-containers +- **Use for:** Production container scripts + +--- + +## Advanced Topics + +### Process Substitution + +**Greg's Wiki:** +- **URL:** https://mywiki.wooledge.org/ProcessSubstitution +- **Use for:** Understanding `<()` syntax + +### Parameter Expansion + +**Bash Hackers Wiki:** +- **URL:** https://wiki.bash-hackers.org/syntax/pe +- **Use for:** Complete parameter expansion reference + +### Regular Expressions + +**Bash Regex:** +- **URL:** https://mywiki.wooledge.org/RegularExpression +- **Use for:** Regex in bash `[[ =~ ]]` + +**PCRE vs POSIX:** +- **URL:** https://www.regular-expressions.info/posix.html +- **Use for:** Understanding regex flavors + +### Parallel Processing + +**GNU Parallel:** +- **URL:** https://www.gnu.org/software/parallel/ +- **Tutorial:** https://www.gnu.org/software/parallel/parallel_tutorial.html +- **Use for:** Parallel command execution + +### Job Control + +**Bash Job Control:** +- **URL:** https://www.gnu.org/software/bash/manual/html_node/Job-Control.html +- **Use for:** Background jobs, job management + +--- + +## Troubleshooting Resources + +### Debugging Tools + +**bashdb** +- **URL:** http://bashdb.sourceforge.net/ +- **Description:** Bash debugger +- **Use for:** Step-by-step debugging + +**xtrace** +```bash +set -x # Enable +set +x # Disable +``` +- **Use for:** Trace command execution + +**PS4 for Better Trace Output** +```bash +export PS4='+(${BASH_SOURCE}:${LINENO}): ${FUNCNAME[0]:+${FUNCNAME[0]}(): }' +set -x +``` + +### Common Issues + +**Bash Pitfalls** +- **URL:** https://mywiki.wooledge.org/BashPitfalls +- **Description:** 50+ common mistakes in bash +- **Use for:** Avoiding and fixing common errors + +**Bash FAQ** +- **URL:** https://mywiki.wooledge.org/BashFAQ +- **Description:** Frequently asked questions +- **Use for:** Quick answers to common questions + +--- + +## Summary: Where to Find Information + +| Question Type | Resource | +|---------------|----------| +| Syntax reference | Bash Manual, DevHints cheat sheet | +| Best practices | Google Shell Style Guide, ShellCheck | +| Portable scripting | POSIX specification, checkbashisms | +| Quick examples | tldr, explainshell.com | +| Common mistakes | Bash Pitfalls, ShellCheck Wiki | +| Advanced topics | Bash Hackers Wiki, Greg's Wiki | +| Testing | BATS documentation | +| Platform differences | Platform-specific docs, Stack Overflow | +| Troubleshooting | Stack Overflow, Unix & Linux SE | +| Learning path | Bash Academy, TLDP guides | + +--- + +## Quick Resource Lookup + +**When writing a new script:** +1. Start with template from Google Style Guide +2. Use ShellCheck while developing +3. Reference Bash Manual for specific features +4. Check Bash Pitfalls for common mistakes + +**When debugging:** +1. Use `set -x` for tracing +2. Check ShellCheck warnings +3. Search Bash Pitfalls +4. Search Stack Overflow for specific error + +**When learning:** +1. Start with Bash Academy or TLDP +2. Use explainshell.com for commands +3. Read Greg's Wiki for in-depth topics +4. Practice with BATS tests + +**When ensuring quality:** +1. Run ShellCheck +2. Run shellcheck +3. Format with shfmt +4. Write BATS tests +5. Review against Google Style Guide + +These resources provide authoritative, up-to-date information for all aspects of bash scripting. diff --git a/skills/bash-master/references/windows-git-bash-paths.md b/skills/bash-master/references/windows-git-bash-paths.md new file mode 100644 index 0000000..114fbf3 --- /dev/null +++ b/skills/bash-master/references/windows-git-bash-paths.md @@ -0,0 +1,798 @@ +# Windows Git Bash / MINGW Path Conversion & Shell Detection + +**CRITICAL KNOWLEDGE FOR BASH SCRIPTING ON WINDOWS** + +This reference provides comprehensive guidance for handling path conversion and shell detection in Git Bash/MINGW/MSYS2 environments on Windows - essential knowledge for cross-platform bash scripting. + +--- + +## Table of Contents + +1. [Path Conversion in Git Bash/MINGW](#path-conversion-in-git-bashMINGW) +2. [Shell Detection Methods](#shell-detection-methods) +3. [Claude Code Specific Issues](#claude-code-specific-issues) +4. [Practical Solutions](#practical-solutions) +5. [Best Practices](#best-practices) + +--- + +## Path Conversion in Git Bash/MINGW + +### Automatic Conversion Behavior + +Git Bash/MINGW automatically converts Unix-style paths to Windows paths when passing arguments to native Windows programs. Understanding this behavior is critical for writing portable scripts. + +**Conversion Rules:** + +```bash +# Unix → Windows path conversion +/foo → C:/Program Files/Git/usr/foo + +# Path lists (colon-separated → semicolon-separated) +/foo:/bar → C:\msys64\foo;C:\msys64\bar + +# Arguments with paths +--dir=/foo → --dir=C:/msys64/foo +``` + +### What Triggers Conversion + +Automatic path conversion is triggered by: + +```bash +# ✓ Leading forward slash (/) in arguments +command /c/Users/username/file.txt + +# ✓ Colon-separated path lists +export PATH=/usr/bin:/usr/local/bin + +# ✓ Arguments after - or , with path components +command --path=/tmp/data +``` + +### What's Exempt from Conversion + +These patterns do NOT trigger automatic conversion: + +```bash +# ✓ Arguments containing = (variable assignments) +VAR=/path/to/something + +# ✓ Drive specifiers (C:) +C:/Windows/System32 + +# ✓ Arguments with ; (already Windows format) +PATH=C:\foo;C:\bar + +# ✓ Arguments starting with // (Windows switches or UNC paths) +//server/share +command //e //s # Command-line switches +``` + +### Control Environment Variables + +**MSYS_NO_PATHCONV** (Git for Windows only): + +```bash +# Disable ALL path conversion +export MSYS_NO_PATHCONV=1 +command /path/to/file + +# Per-command usage (recommended) +MSYS_NO_PATHCONV=1 command /path/to/file + +# Value doesn't matter, just needs to be defined +MSYS_NO_PATHCONV=0 # Still disables conversion +``` + +**MSYS2_ARG_CONV_EXCL** (MSYS2 only): + +```bash +# Exclude everything +export MSYS2_ARG_CONV_EXCL="*" + +# Exclude specific prefixes +export MSYS2_ARG_CONV_EXCL="--dir=;/test" + +# Multiple patterns (semicolon-separated) +export MSYS2_ARG_CONV_EXCL="--path=;--config=;/tmp" +``` + +**MSYS2_ENV_CONV_EXCL**: + +```bash +# Prevents environment variable conversion +# Same syntax as MSYS2_ARG_CONV_EXCL +export MSYS2_ENV_CONV_EXCL="MY_PATH;CONFIG_DIR" +``` + +### Manual Conversion with cygpath + +The `cygpath` utility provides precise control over path conversion: + +```bash +# Convert Windows → Unix format +unix_path=$(cygpath -u "C:\Users\username\file.txt") +# Result: /c/Users/username/file.txt + +# Convert Unix → Windows format +windows_path=$(cygpath -w "/c/Users/username/file.txt") +# Result: C:\Users\username\file.txt + +# Convert to mixed format (forward slashes, Windows drive) +mixed_path=$(cygpath -m "/c/Users/username/file.txt") +# Result: C:/Users/username/file.txt + +# Convert absolute path +absolute_path=$(cygpath -a "relative/path") + +# Convert multiple paths +cygpath -u "C:\path1" "C:\path2" +``` + +**Practical cygpath usage:** + +```bash +#!/usr/bin/env bash +# Cross-platform path handling + +get_native_path() { + local path="$1" + + # Check if running on Windows (Git Bash/MINGW) + if [[ "$OSTYPE" == "msys" ]] || [[ "$OSTYPE" == "mingw"* ]]; then + # Convert to Windows format for native programs + cygpath -w "$path" + else + # Already Unix format on Linux/macOS + echo "$path" + fi +} + +# Usage +native_path=$(get_native_path "/c/Users/data") +windows_program.exe "$native_path" +``` + +### Common Workarounds + +When automatic conversion causes issues: + +**1. Use double slashes:** + +```bash +# Problem: /e gets converted to C:/Program Files/Git/e +command /e /s + +# Solution: Use double slashes +command //e //s # Treated as switches, not paths +``` + +**2. Use dash notation:** + +```bash +# Problem: /e flag converted to path +command /e /s + +# Solution: Use dash notation +command -e -s +``` + +**3. Set MSYS_NO_PATHCONV temporarily:** + +```bash +# Disable conversion for single command +MSYS_NO_PATHCONV=1 command /path/with/special/chars + +# Or export for script section +export MSYS_NO_PATHCONV=1 +command1 /path1 +command2 /path2 +unset MSYS_NO_PATHCONV +``` + +**4. Quote paths with spaces:** + +```bash +# Always quote paths with spaces +command "/c/Program Files/App/file.txt" + +# Or escape spaces +command /c/Program\ Files/App/file.txt +``` + +--- + +## Shell Detection Methods + +### Method 1: $OSTYPE (Fastest, Bash-Only) + +Best for: Quick platform detection in bash scripts + +```bash +#!/usr/bin/env bash + +case "$OSTYPE" in + linux-gnu*) + echo "Linux" + ;; + darwin*) + echo "macOS" + ;; + cygwin*) + echo "Cygwin" + ;; + msys*) + echo "MSYS/Git Bash/MinGW" + # Most common in Git for Windows + ;; + win*) + echo "Windows (native)" + ;; + *) + echo "Unknown: $OSTYPE" + ;; +esac +``` + +**Advantages:** +- Fast (shell variable, no external command) +- Reliable for bash +- No forking required + +**Disadvantages:** +- Bash-specific (not available in POSIX sh) +- Less detailed than uname + +### Method 2: uname -s (Most Portable) + +Best for: Maximum portability and detailed information + +```bash +#!/bin/sh +# Works in any POSIX shell + +case "$(uname -s)" in + Darwin*) + echo "macOS" + ;; + Linux*) + # Check for WSL + if grep -qi microsoft /proc/version 2>/dev/null; then + echo "Windows Subsystem for Linux (WSL)" + else + echo "Linux (native)" + fi + ;; + CYGWIN*) + echo "Cygwin" + ;; + MINGW64*) + echo "Git Bash 64-bit / MINGW64" + ;; + MINGW32*) + echo "Git Bash 32-bit / MINGW32" + ;; + MSYS_NT*) + echo "MSYS" + ;; + *) + echo "Unknown: $(uname -s)" + ;; +esac +``` + +**Common uname -s outputs:** + +| Output | Platform | Description | +|--------|----------|-------------| +| `Darwin` | macOS | All macOS versions | +| `Linux` | Linux/WSL | Check `/proc/version` for WSL | +| `MINGW64_NT-10.0-*` | Git Bash | Git for Windows (64-bit) | +| `MINGW32_NT-10.0-*` | Git Bash | Git for Windows (32-bit) | +| `CYGWIN_NT-*` | Cygwin | Cygwin environment | +| `MSYS_NT-*` | MSYS | MSYS environment | + +**Advantages:** +- Works in any POSIX shell +- Detailed system information +- Standard on all Unix-like systems + +**Disadvantages:** +- Requires forking (slower than $OSTYPE) +- Output format varies by OS version + +### Method 3: $MSYSTEM (MSYS2/Git Bash Specific) + +Best for: Detecting MINGW subsystem type + +```bash +#!/usr/bin/env bash + +case "$MSYSTEM" in + MINGW64) + echo "Native Windows 64-bit environment" + # Build native Windows 64-bit applications + ;; + MINGW32) + echo "Native Windows 32-bit environment" + # Build native Windows 32-bit applications + ;; + MSYS) + echo "POSIX-compliant environment" + # Build POSIX applications (depend on msys-2.0.dll) + ;; + "") + echo "Not running in MSYS2/Git Bash" + ;; + *) + echo "Unknown MSYSTEM: $MSYSTEM" + ;; +esac +``` + +**MSYSTEM Values:** + +| Value | Purpose | Path Conversion | Libraries | +|-------|---------|-----------------|-----------| +| `MINGW64` | Native Windows 64-bit | Automatic | Windows native (mingw-w64) | +| `MINGW32` | Native Windows 32-bit | Automatic | Windows native (mingw) | +| `MSYS` | POSIX environment | Minimal | POSIX (msys-2.0.dll) | + +**WARNING:** Never set `$MSYSTEM` manually outside of MSYS2/Git Bash shells! It's automatically set by the environment and changing it can break the system. + +**Advantages:** +- Precise subsystem detection +- Important for build systems +- Fast (environment variable) + +**Disadvantages:** +- Only available in MSYS2/Git Bash +- Not set on other platforms + +### Comprehensive Detection Function + +Combine all methods for robust detection: + +```bash +#!/usr/bin/env bash + +detect_platform() { + local platform="" + local details="" + + # Check MSYSTEM first (most specific for Git Bash) + if [[ -n "${MSYSTEM:-}" ]]; then + platform="gitbash" + details="$MSYSTEM" + echo "platform=$platform subsystem=$MSYSTEM" + return 0 + fi + + # Check OSTYPE + case "$OSTYPE" in + linux-gnu*) + # Distinguish WSL from native Linux + if grep -qi microsoft /proc/version 2>/dev/null; then + platform="wsl" + if [[ -n "${WSL_DISTRO_NAME:-}" ]]; then + details="$WSL_DISTRO_NAME" + fi + else + platform="linux" + fi + ;; + darwin*) + platform="macos" + ;; + msys*|mingw*|cygwin*) + platform="gitbash" + ;; + *) + # Fallback to uname + case "$(uname -s 2>/dev/null)" in + MINGW*|MSYS*) + platform="gitbash" + ;; + CYGWIN*) + platform="cygwin" + ;; + *) + platform="unknown" + ;; + esac + ;; + esac + + echo "platform=$platform${details:+ details=$details}" +} + +# Usage +platform_info=$(detect_platform) +echo "$platform_info" +``` + +--- + +## Claude Code Specific Issues + +### Issue #2602: Snapshot Path Conversion Failure + +**Problem:** +``` +/usr/bin/bash: line 1: C:UsersDavid...No such file +``` + +**Root Cause:** +- Node.js `os.tmpdir()` returns Windows paths (e.g., `C:\Users\...`) +- Git Bash expects Unix paths (e.g., `/c/Users/...`) +- Automatic conversion fails due to path format mismatch + +**Solution (Claude Code v1.0.51+):** + +Set environment variable before starting Claude Code: + +```powershell +# PowerShell +$env:CLAUDE_CODE_GIT_BASH_PATH = "C:\Program Files\git\bin\bash.exe" +``` + +```cmd +# CMD +set CLAUDE_CODE_GIT_BASH_PATH=C:\Program Files\git\bin\bash.exe +``` + +```bash +# Git Bash (add to ~/.bashrc) +export CLAUDE_CODE_GIT_BASH_PATH="C:\\Program Files\\git\\bin\\bash.exe" +``` + +**Note:** Versions 1.0.72+ reportedly work without modifications, but setting the environment variable ensures compatibility. + +### Other Known Issues + +**Drive letter duplication:** +```bash +# Problem +cd D:\dev +pwd +# Output: D:\d\dev (incorrect) + +# Solution: Use Unix-style path in Git Bash +cd /d/dev +pwd +# Output: /d/dev +``` + +**Spaces in paths:** +```bash +# Problem: Unquoted path with spaces +cd C:\Program Files\App # Fails + +# Solution: Always quote paths with spaces +cd "C:\Program Files\App" +cd /c/Program\ Files/App +``` + +**VS Code extension Git Bash detection:** + +VS Code may not auto-detect Git Bash. Configure manually in settings: + +```json +{ + "terminal.integrated.defaultProfile.windows": "Git Bash", + "terminal.integrated.profiles.windows": { + "Git Bash": { + "path": "C:\\Program Files\\Git\\bin\\bash.exe" + } + } +} +``` + +--- + +## Practical Solutions + +### Cross-Platform Path Handling Function + +```bash +#!/usr/bin/env bash + +# Convert path to format appropriate for current platform +normalize_path_for_platform() { + local path="$1" + + case "$OSTYPE" in + msys*|mingw*) + # On Git Bash, convert to Unix format if Windows format provided + if [[ "$path" =~ ^[A-Z]:\\ ]]; then + # Windows path detected, convert to Unix + path=$(cygpath -u "$path" 2>/dev/null || echo "$path") + fi + ;; + *) + # On Linux/macOS, path is already correct + ;; + esac + + echo "$path" +} + +# Convert path to native format for external programs +convert_to_native_path() { + local path="$1" + + case "$OSTYPE" in + msys*|mingw*) + # Convert to Windows format for native Windows programs + cygpath -w "$path" 2>/dev/null || echo "$path" + ;; + *) + # Already native on Linux/macOS + echo "$path" + ;; + esac +} + +# Example usage +input_path="/c/Users/username/file.txt" +normalized=$(normalize_path_for_platform "$input_path") +echo "Normalized: $normalized" + +native=$(convert_to_native_path "$normalized") +echo "Native: $native" +``` + +### Script Template for Windows Compatibility + +```bash +#!/usr/bin/env bash +set -euo pipefail + +# Detect if running on Git Bash/MINGW +is_git_bash() { + [[ "$OSTYPE" == "msys" ]] || [[ "$OSTYPE" == "mingw"* ]] +} + +# Handle path conversion based on platform +get_path() { + local path="$1" + + if is_git_bash; then + # Ensure Unix format in Git Bash + if [[ "$path" =~ ^[A-Z]:\\ ]]; then + cygpath -u "$path" + else + echo "$path" + fi + else + echo "$path" + fi +} + +# Call Windows program from Git Bash +call_windows_program() { + local program="$1" + shift + local args=("$@") + + if is_git_bash; then + # Disable path conversion for complex arguments + MSYS_NO_PATHCONV=1 "$program" "${args[@]}" + else + "$program" "${args[@]}" + fi +} + +# Main script logic +main() { + local file_path="$1" + + # Normalize path + file_path=$(get_path "$file_path") + + # Process file + echo "Processing: $file_path" + + # Call Windows program if needed + if is_git_bash; then + local native_path + native_path=$(cygpath -w "$file_path") + call_windows_program notepad.exe "$native_path" + fi +} + +main "$@" +``` + +### Handling Command-Line Arguments + +```bash +#!/usr/bin/env bash + +# Parse arguments that might contain paths +parse_arguments() { + while [[ $# -gt 0 ]]; do + case "$1" in + --path=*) + local path="${1#*=}" + # Disable conversion for this specific argument pattern + MSYS2_ARG_CONV_EXCL="--path=" command --path="$path" + shift + ;; + --dir) + local dir="$2" + # Use converted path + local native_dir + if command -v cygpath &>/dev/null; then + native_dir=$(cygpath -w "$dir") + else + native_dir="$dir" + fi + command --dir "$native_dir" + shift 2 + ;; + *) + shift + ;; + esac + done +} +``` + +--- + +## Best Practices + +### 1. Always Quote Paths + +```bash +# ✗ WRONG - Breaks with spaces +cd $path + +# ✓ CORRECT - Works with all paths +cd "$path" +``` + +### 2. Use cygpath for Reliable Conversion + +```bash +# ✗ WRONG - Manual conversion is error-prone +path="${path//\\/\/}" +path="${path/C:/\/c}" + +# ✓ CORRECT - Use cygpath +path=$(cygpath -u "$path") +``` + +### 3. Detect Platform Before Path Operations + +```bash +# ✓ CORRECT - Platform-aware +if [[ "$OSTYPE" == "msys" ]] || [[ "$OSTYPE" == "mingw"* ]]; then + # Git Bash specific handling + path=$(cygpath -u "$windows_path") +else + # Linux/macOS handling + path="$unix_path" +fi +``` + +### 4. Use MSYS_NO_PATHCONV Sparingly + +```bash +# ✗ WRONG - Disables all conversion globally +export MSYS_NO_PATHCONV=1 + +# ✓ CORRECT - Per-command when needed +MSYS_NO_PATHCONV=1 command --flag=/value +``` + +### 5. Test on Target Platform + +Always test scripts on Windows with Git Bash if that's a target platform: + +```bash +# Test script +bash -n script.sh # Syntax check +shellcheck script.sh # Static analysis +bash script.sh # Run on actual platform +``` + +### 6. Document Platform Requirements + +```bash +#!/usr/bin/env bash +# +# Platform Support: +# - Linux: Full support +# - macOS: Full support +# - Windows Git Bash: Requires Git for Windows 2.x+ +# - Windows WSL: Full support +# +# Known Issues: +# - Path conversion may occur when calling Windows programs from Git Bash +# - Use MSYS_NO_PATHCONV=1 if experiencing path-related errors +``` + +### 7. Use Forward Slashes in Git Bash + +```bash +# ✓ PREFERRED - Works in all environments +cd /c/Users/username/project + +# ✗ AVOID - Requires escaping or quoting +cd "C:\Users\username\project" +cd C:\\Users\\username\\project +``` + +### 8. Check for cygpath Availability + +```bash +# Graceful fallback if cygpath not available +convert_path() { + local path="$1" + + if command -v cygpath &>/dev/null; then + cygpath -u "$path" + else + # Manual conversion as fallback + echo "$path" | sed 's|\\|/|g' | sed 's|^\([A-Z]\):|/\L\1|' + fi +} +``` + +--- + +## Quick Reference Card + +### Path Conversion Control + +| Variable | Scope | Effect | +|----------|-------|--------| +| `MSYS_NO_PATHCONV=1` | Git for Windows | Disables all conversion | +| `MSYS2_ARG_CONV_EXCL="pattern"` | MSYS2 | Excludes specific patterns | +| `MSYS2_ENV_CONV_EXCL="var"` | MSYS2 | Excludes environment variables | + +### Shell Detection Variables + +| Variable | Available | Purpose | +|----------|-----------|---------| +| `$OSTYPE` | Bash | Quick OS type detection | +| `$MSYSTEM` | MSYS2/Git Bash | Subsystem type (MINGW64/MINGW32/MSYS) | +| `$(uname -s)` | All POSIX | Detailed OS identification | + +### cygpath Quick Reference + +| Command | Purpose | +|---------|---------| +| `cygpath -u "C:\path"` | Windows → Unix format | +| `cygpath -w "/c/path"` | Unix → Windows format | +| `cygpath -m "/c/path"` | Unix → Mixed format (forward slashes) | +| `cygpath -a "path"` | Convert to absolute path | + +### Common Issues & Solutions + +| Problem | Solution | +|---------|----------| +| Path with spaces breaks | Quote the path: `"$path"` | +| Flag `/e` converted to path | Use `//e` or `-e` instead | +| Drive duplication `D:\d\` | Use Unix format: `/d/` | +| Windows program needs Windows path | Use `cygpath -w "$unix_path"` | +| Script fails in Claude Code | Set `CLAUDE_CODE_GIT_BASH_PATH` | + +--- + +## Summary + +Understanding Git Bash/MINGW path conversion is essential for writing robust cross-platform bash scripts that work on Windows. Key takeaways: + +1. **Automatic conversion** happens for Unix-style paths in arguments +2. **Control conversion** using `MSYS_NO_PATHCONV` and `MSYS2_ARG_CONV_EXCL` +3. **Use cygpath** for reliable manual path conversion +4. **Detect platform** using `$OSTYPE`, `$MSYSTEM`, or `uname -s` +5. **Quote all paths** to handle spaces and special characters +6. **Test on target platforms** to catch platform-specific issues +7. **Document requirements** so users know what to expect + +With this knowledge, you can write bash scripts that work seamlessly across Linux, macOS, Windows Git Bash, WSL, and other Unix-like environments. diff --git a/skills/debugging-troubleshooting-2025.md b/skills/debugging-troubleshooting-2025.md new file mode 100644 index 0000000..3ad7983 --- /dev/null +++ b/skills/debugging-troubleshooting-2025.md @@ -0,0 +1,686 @@ +--- +name: debugging-troubleshooting-2025 +description: Comprehensive bash script debugging and troubleshooting techniques for 2025 +--- + +## 🚨 CRITICAL GUIDELINES + +### Windows File Path Requirements + +**MANDATORY: Always Use Backslashes on Windows for File Paths** + +When using Edit or Write tools on Windows, you MUST use backslashes (`\`) in file paths, NOT forward slashes (`/`). + +**Examples:** +- ❌ WRONG: `D:/repos/project/file.tsx` +- ✅ CORRECT: `D:\repos\project\file.tsx` + +This applies to: +- Edit tool file_path parameter +- Write tool file_path parameter +- All file operations on Windows systems + + +### Documentation Guidelines + +**NEVER create new documentation files unless explicitly requested by the user.** + +- **Priority**: Update existing README.md files rather than creating new documentation +- **Repository cleanliness**: Keep repository root clean - only README.md unless user requests otherwise +- **Style**: Documentation should be concise, direct, and professional - avoid AI-generated tone +- **User preference**: Only create additional .md files when user specifically asks for documentation + + +--- + +# Bash Debugging & Troubleshooting (2025) + +## Overview + +Comprehensive debugging techniques and troubleshooting patterns for bash scripts following 2025 best practices. + +## Debug Mode Techniques + +### 1. Basic Debug Mode (set -x) + +```bash +#!/usr/bin/env bash +set -euo pipefail + +# Enable debug mode +set -x + +# Your commands here +command1 +command2 + +# Disable debug mode +set +x + +# Continue without debug +command3 +``` + +### 2. Enhanced Debug Output (PS4) + +```bash +#!/usr/bin/env bash +set -euo pipefail + +# Custom debug prompt with file:line:function +export PS4='+(${BASH_SOURCE}:${LINENO}): ${FUNCNAME[0]:+${FUNCNAME[0]}(): }' + +set -x +my_function() { + local var="value" + echo "$var" +} +my_function +set +x +``` + +**Output:** +``` ++(script.sh:10): my_function(): local var=value ++(script.sh:11): my_function(): echo value +value +``` + +### 3. Conditional Debugging + +```bash +#!/usr/bin/env bash +set -euo pipefail + +# Enable via environment variable +DEBUG="${DEBUG:-false}" + +debug() { + if [[ "$DEBUG" == "true" ]]; then + echo "[DEBUG] $*" >&2 + fi +} + +# Usage +debug "Starting process" +process_data +debug "Process complete" + +# Run: DEBUG=true ./script.sh +``` + +### 4. Debugging Specific Functions + +```bash +#!/usr/bin/env bash +set -euo pipefail + +# Debug wrapper +debug_function() { + local func_name="$1" + shift + + echo "[TRACE] Calling: $func_name $*" >&2 + set -x + "$func_name" "$@" + local exit_code=$? + set +x + echo "[TRACE] Exit code: $exit_code" >&2 + return $exit_code +} + +# Usage +my_complex_function() { + local arg1="$1" + # Complex logic + echo "Result: $arg1" +} + +debug_function my_complex_function "test" +``` + +## Tracing and Profiling + +### 1. Execution Time Profiling + +```bash +#!/usr/bin/env bash +set -euo pipefail + +# Profile function execution time +profile() { + local start_ns end_ns duration_ms + start_ns=$(date +%s%N) + + "$@" + local exit_code=$? + + end_ns=$(date +%s%N) + duration_ms=$(( (end_ns - start_ns) / 1000000 )) + + echo "[PROFILE] '$*' took ${duration_ms}ms (exit: $exit_code)" >&2 + return $exit_code +} + +# Usage +profile slow_command arg1 arg2 +``` + +### 2. Function Call Tracing + +```bash +#!/usr/bin/env bash +set -euo pipefail + +# Trace all function calls +trace_on() { + set -o functrace + trap 'echo "[TRACE] ${FUNCNAME[0]}() called from ${BASH_SOURCE[1]}:${BASH_LINENO[0]}" >&2' DEBUG +} + +trace_off() { + set +o functrace + trap - DEBUG +} + +# Usage +trace_on +function1 +function2 +trace_off +``` + +### 3. Variable Inspection + +```bash +#!/usr/bin/env bash +set -euo pipefail + +# Inspect all variables at any point +inspect_vars() { + echo "=== Variable Dump ===" >&2 + declare -p | grep -v "^declare -[^ ]*r " | sort >&2 + echo "===================" >&2 +} + +# Inspect specific variable +inspect_var() { + local var_name="$1" + echo "[INSPECT] $var_name = ${!var_name:-}" >&2 +} + +# Usage +my_var="test" +inspect_var my_var +inspect_vars +``` + +## Error Handling and Recovery + +### 1. Trap-Based Error Handler + +```bash +#!/usr/bin/env bash +set -euo pipefail + +# Comprehensive error handler +error_handler() { + local exit_code=$? + local line_number=$1 + + echo "ERROR: Command failed with exit code $exit_code" >&2 + echo " File: ${BASH_SOURCE[1]}" >&2 + echo " Line: $line_number" >&2 + echo " Function: ${FUNCNAME[1]:-main}" >&2 + + # Print stack trace + local frame=0 + while caller $frame; do + ((frame++)) + done >&2 + + exit "$exit_code" +} + +trap 'error_handler $LINENO' ERR + +# Your script logic +risky_command +``` + +### 2. Dry-Run Mode + +```bash +#!/usr/bin/env bash +set -euo pipefail + +DRY_RUN="${DRY_RUN:-false}" + +# Safe execution wrapper +execute() { + if [[ "$DRY_RUN" == "true" ]]; then + echo "[DRY-RUN] Would execute: $*" >&2 + return 0 + else + "$@" + fi +} + +# Usage +execute rm -rf /tmp/data +execute cp file.txt backup/ + +# Run: DRY_RUN=true ./script.sh +``` + +### 3. Rollback on Failure + +```bash +#!/usr/bin/env bash +set -euo pipefail + +OPERATIONS=() + +# Track operations for rollback +track_operation() { + local rollback_cmd="$1" + OPERATIONS+=("$rollback_cmd") +} + +# Execute rollback +rollback() { + echo "Rolling back operations..." >&2 + for ((i=${#OPERATIONS[@]}-1; i>=0; i--)); do + echo " Executing: ${OPERATIONS[$i]}" >&2 + eval "${OPERATIONS[$i]}" || true + done +} + +trap rollback ERR EXIT + +# Example usage +mkdir /tmp/mydir +track_operation "rmdir /tmp/mydir" + +touch /tmp/mydir/file.txt +track_operation "rm /tmp/mydir/file.txt" + +# If script fails, rollback executes automatically +``` + +## Common Issues and Solutions + +### 1. Script Works Interactively but Fails in Cron + +**Problem:** Script runs fine manually but fails when scheduled. + +**Solution:** +```bash +#!/usr/bin/env bash +set -euo pipefail + +# Fix PATH for cron +export PATH="/usr/local/bin:/usr/bin:/bin" + +# Set working directory +cd "$(dirname "$0")" || exit 1 + +# Log everything for debugging +exec 1>> /var/log/myscript.log 2>&1 + +echo "[$(date)] Script starting" +# Your commands here +echo "[$(date)] Script complete" +``` + +### 2. Whitespace in Filenames Breaking Script + +**Problem:** Script fails when processing files with spaces. + +**Debugging:** +```bash +#!/usr/bin/env bash +set -euo pipefail + +# Show exactly what the script sees +debug_filename() { + local filename="$1" + echo "Filename: '$filename'" >&2 + echo "Length: ${#filename}" >&2 + hexdump -C <<< "$filename" >&2 +} + +# Proper handling +while IFS= read -r -d '' file; do + debug_filename "$file" + # Process "$file" +done < <(find . -name "*.txt" -print0) +``` + +### 3. Script Behaves Differently on Different Systems + +**Problem:** Works on Linux but fails on macOS. + +**Debugging:** +```bash +#!/usr/bin/env bash +set -euo pipefail + +# Platform detection and debugging +detect_platform() { + echo "=== Platform Info ===" >&2 + echo "OS: $OSTYPE" >&2 + echo "Bash: $BASH_VERSION" >&2 + echo "PATH: $PATH" >&2 + + # Check tool versions + for tool in sed awk grep; do + if command -v "$tool" &> /dev/null; then + echo "$tool: $($tool --version 2>&1 | head -1)" >&2 + fi + done + echo "====================" >&2 +} + +detect_platform + +# Use portable patterns +case "$OSTYPE" in + linux*) SED_CMD="sed" ;; + darwin*) SED_CMD=$(command -v gsed || echo sed) ;; + *) echo "Unknown platform" >&2; exit 1 ;; +esac +``` + +### 4. Variable Scope Issues + +**Problem:** Variables not available where expected. + +**Debugging:** +```bash +#!/usr/bin/env bash +set -euo pipefail + +# Show variable scope +test_scope() { + local local_var="local" + global_var="global" + + echo "Inside function:" >&2 + echo " local_var=$local_var" >&2 + echo " global_var=$global_var" >&2 +} + +test_scope + +echo "Outside function:" >&2 +echo " local_var=${local_var:-}" >&2 +echo " global_var=${global_var:-}" >&2 + +# Subshell scope issue +echo "test" | ( + read -r value + echo "In subshell: $value" +) +echo "After subshell: ${value:-}" # Empty! +``` + +## Interactive Debugging + +### 1. Breakpoint Pattern + +```bash +#!/usr/bin/env bash +set -euo pipefail + +# Interactive breakpoint +breakpoint() { + local message="${1:-Breakpoint}" + echo "$message" >&2 + echo "Variables:" >&2 + declare -p | grep -v "^declare -[^ ]*r " >&2 + + read -rp "Press Enter to continue, 'i' for inspect: " choice + if [[ "$choice" == "i" ]]; then + bash # Drop into interactive shell + fi +} + +# Usage +value=42 +breakpoint "Before critical operation" +critical_operation "$value" +``` + +### 2. Watch Mode (Continuous Debugging) + +```bash +#!/usr/bin/env bash + +# Watch script execution in real-time +watch_script() { + local script="$1" + shift + + while true; do + clear + echo "=== Running: $script $* ===" + echo "=== $(date) ===" + bash -x "$script" "$@" 2>&1 | tail -50 + sleep 2 + done +} + +# Usage: watch_script myscript.sh arg1 arg2 +``` + +## Logging Best Practices + +### 1. Structured Logging + +```bash +#!/usr/bin/env bash +set -euo pipefail + +readonly LOG_FILE="${LOG_FILE:-/var/log/myscript.log}" + +log() { + local level="$1" + shift + local timestamp + timestamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ") + + echo "${timestamp} [${level}] $*" | tee -a "$LOG_FILE" >&2 +} + +log_info() { log "INFO" "$@"; } +log_warn() { log "WARN" "$@"; } +log_error() { log "ERROR" "$@"; } +log_debug() { [[ "${DEBUG:-false}" == "true" ]] && log "DEBUG" "$@"; } + +# Usage +log_info "Starting process" +log_debug "Debug info" +log_error "Something failed" +``` + +### 2. Log Rotation Awareness + +```bash +#!/usr/bin/env bash +set -euo pipefail + +# Ensure log file exists and is writable +setup_logging() { + local log_file="${1:-/var/log/myscript.log}" + local log_dir + log_dir=$(dirname "$log_file") + + if [[ ! -d "$log_dir" ]]; then + mkdir -p "$log_dir" || { + echo "Cannot create log directory: $log_dir" >&2 + return 1 + } + fi + + if [[ ! -w "$log_dir" ]]; then + echo "Log directory not writable: $log_dir" >&2 + return 1 + fi + + # Redirect all output to log + exec 1>> "$log_file" + exec 2>&1 +} + +setup_logging +``` + +## Performance Debugging + +### 1. Identify Slow Commands + +```bash +#!/usr/bin/env bash +set -euo pipefail + +# Profile each command in script +profile_script() { + export PS4='+ $(date +%s.%N) ${BASH_SOURCE}:${LINENO}: ' + set -x + + # Your commands here + command1 + command2 + command3 + + set +x +} + +# Analyze output: +# + 1698765432.123456 script.sh:10: command1 (fast) +# + 1698765437.654321 script.sh:11: command2 (5 seconds - slow!) +``` + +### 2. Memory Usage Tracking + +```bash +#!/usr/bin/env bash +set -euo pipefail + +# Track memory usage +check_memory() { + local pid=${1:-$$} + ps -o pid,vsz,rss,comm -p "$pid" | tail -1 +} + +# Monitor during execution +monitor_memory() { + while true; do + check_memory + sleep 1 + done & + local monitor_pid=$! + + # Your commands here + "$@" + + kill "$monitor_pid" 2>/dev/null || true + wait "$monitor_pid" 2>/dev/null || true +} + +monitor_memory ./memory_intensive_task.sh +``` + +## Testing Patterns + +### 1. Unit Test Template + +```bash +#!/usr/bin/env bash +# test_functions.sh + +# Source the script to test +source ./functions.sh + +# Test counter +TESTS_RUN=0 +TESTS_PASSED=0 +TESTS_FAILED=0 + +# Assert function +assert_equals() { + local expected="$1" + local actual="$2" + local test_name="${3:-Test}" + + ((TESTS_RUN++)) + + if [[ "$expected" == "$actual" ]]; then + echo "✓ $test_name" >&2 + ((TESTS_PASSED++)) + else + echo "✗ $test_name" >&2 + echo " Expected: $expected" >&2 + echo " Actual: $actual" >&2 + ((TESTS_FAILED++)) + fi +} + +# Run tests +test_add_numbers() { + local result + result=$(add_numbers 2 3) + assert_equals "5" "$result" "add_numbers 2 3" +} + +test_add_numbers + +# Summary +echo "========================================" >&2 +echo "Tests run: $TESTS_RUN" >&2 +echo "Passed: $TESTS_PASSED" >&2 +echo "Failed: $TESTS_FAILED" >&2 + +[[ $TESTS_FAILED -eq 0 ]] +``` + +## ShellCheck Integration + +```bash +#!/usr/bin/env bash +set -euo pipefail + +# Validate script with ShellCheck +validate_script() { + local script="$1" + + if ! command -v shellcheck &> /dev/null; then + echo "ShellCheck not installed" >&2 + return 1 + fi + + echo "Running ShellCheck on $script..." >&2 + if shellcheck --severity=warning "$script"; then + echo "✓ ShellCheck passed" >&2 + return 0 + else + echo "✗ ShellCheck failed" >&2 + return 1 + fi +} + +# Usage +validate_script myscript.sh +``` + +## Resources + +- [Bash Hackers Wiki - Debugging](https://wiki.bash-hackers.org/scripting/debuggingtips) +- [ShellCheck](https://www.shellcheck.net/) +- [BATS Testing Framework](https://github.com/bats-core/bats-core) +- [Bash Reference Manual - Debugging](https://www.gnu.org/software/bash/manual/html_node/The-Set-Builtin.html) + +--- + +**Effective debugging requires systematic approaches, comprehensive logging, and proper tooling. Master these techniques for production-ready bash scripts in 2025.** diff --git a/skills/modern-automation-patterns.md b/skills/modern-automation-patterns.md new file mode 100644 index 0000000..43432a5 --- /dev/null +++ b/skills/modern-automation-patterns.md @@ -0,0 +1,691 @@ +--- +name: modern-automation-patterns +description: Modern DevOps and CI/CD automation patterns with containers and cloud (2025) +--- + +## 🚨 CRITICAL GUIDELINES + +### Windows File Path Requirements + +**MANDATORY: Always Use Backslashes on Windows for File Paths** + +When using Edit or Write tools on Windows, you MUST use backslashes (`\`) in file paths, NOT forward slashes (`/`). + +**Examples:** +- ❌ WRONG: `D:/repos/project/file.tsx` +- ✅ CORRECT: `D:\repos\project\file.tsx` + +This applies to: +- Edit tool file_path parameter +- Write tool file_path parameter +- All file operations on Windows systems + + +### Documentation Guidelines + +**NEVER create new documentation files unless explicitly requested by the user.** + +- **Priority**: Update existing README.md files rather than creating new documentation +- **Repository cleanliness**: Keep repository root clean - only README.md unless user requests otherwise +- **Style**: Documentation should be concise, direct, and professional - avoid AI-generated tone +- **User preference**: Only create additional .md files when user specifically asks for documentation + + +--- + +# Modern Automation Patterns (2025) + +## Overview + +Production-ready patterns for DevOps automation, CI/CD pipelines, and cloud-native operations following 2025 industry standards. + +## Container-Aware Scripting + +### Docker/Kubernetes Detection + +```bash +#!/usr/bin/env bash +set -euo pipefail + +# Detect container environment +detect_container() { + if [[ -f /.dockerenv ]]; then + echo "docker" + elif grep -q docker /proc/1/cgroup 2>/dev/null; then + echo "docker" + elif [[ -n "${KUBERNETES_SERVICE_HOST:-}" ]]; then + echo "kubernetes" + else + echo "host" + fi +} + +# Container-aware configuration +readonly CONTAINER_ENV=$(detect_container) + +case "$CONTAINER_ENV" in + docker|kubernetes) + # Container-specific paths + DATA_DIR="/data" + CONFIG_DIR="/config" + ;; + host) + # Host-specific paths + DATA_DIR="/var/lib/app" + CONFIG_DIR="/etc/app" + ;; +esac +``` + +### Minimal Container Scripts + +```bash +#!/bin/sh +# Use /bin/sh for Alpine-based containers (no bash) + +set -eu # Note: No pipefail in POSIX sh + +# Check if running as PID 1 +if [ $$ -eq 1 ]; then + # PID 1 must handle signals properly + trap 'kill -TERM $child 2>/dev/null' TERM INT + + # Start main process in background + /app/main & + child=$! + + # Wait for child + wait "$child" +else + # Not PID 1, run directly + exec /app/main +fi +``` + +### Health Check Scripts + +```bash +#!/usr/bin/env bash +# healthcheck.sh - Container health probe + +set -euo pipefail + +# Quick health check (< 1 second) +check_health() { + local timeout=1 + + # Check process is running + if ! pgrep -f "myapp" > /dev/null; then + echo "Process not running" >&2 + return 1 + fi + + # Check HTTP endpoint + if ! timeout "$timeout" curl -sf http://localhost:8080/health > /dev/null; then + echo "Health endpoint failed" >&2 + return 1 + fi + + # Check critical files + if [[ ! -f /app/ready ]]; then + echo "Not ready" >&2 + return 1 + fi + + return 0 +} + +check_health +``` + +## CI/CD Pipeline Patterns + +### GitHub Actions Helper + +```bash +#!/usr/bin/env bash +# ci-helper.sh - GitHub Actions utilities + +set -euo pipefail + +# Detect GitHub Actions +is_github_actions() { + [[ "${GITHUB_ACTIONS:-false}" == "true" ]] +} + +# GitHub Actions output +gh_output() { + local name="$1" + local value="$2" + + if is_github_actions; then + echo "${name}=${value}" >> "$GITHUB_OUTPUT" + fi +} + +# GitHub Actions annotations +gh_error() { + if is_github_actions; then + echo "::error::$*" + else + echo "ERROR: $*" >&2 + fi +} + +gh_warning() { + if is_github_actions; then + echo "::warning::$*" + else + echo "WARN: $*" >&2 + fi +} + +gh_notice() { + if is_github_actions; then + echo "::notice::$*" + else + echo "INFO: $*" + fi +} + +# Set job summary +gh_summary() { + if is_github_actions; then + echo "$*" >> "$GITHUB_STEP_SUMMARY" + fi +} + +# Usage example +gh_notice "Starting build" +build_result=$(make build 2>&1) +gh_output "build_result" "$build_result" +gh_summary "## Build Complete\n\nStatus: Success" +``` + +### Azure DevOps Helper + +```bash +#!/usr/bin/env bash +# azdo-helper.sh - Azure DevOps utilities + +set -euo pipefail + +# Detect Azure DevOps +is_azure_devops() { + [[ -n "${TF_BUILD:-}" ]] +} + +# Azure DevOps output variable +azdo_output() { + local name="$1" + local value="$2" + + if is_azure_devops; then + echo "##vso[task.setvariable variable=$name]$value" + fi +} + +# Azure DevOps logging +azdo_error() { + if is_azure_devops; then + echo "##vso[task.logissue type=error]$*" + else + echo "ERROR: $*" >&2 + fi +} + +azdo_warning() { + if is_azure_devops; then + echo "##vso[task.logissue type=warning]$*" + else + echo "WARN: $*" >&2 + fi +} + +# Section grouping +azdo_section_start() { + is_azure_devops && echo "##[section]$*" +} + +azdo_section_end() { + is_azure_devops && echo "##[endsection]" +} + +# Usage +azdo_section_start "Running Tests" +test_result=$(npm test) +azdo_output "test_result" "$test_result" +azdo_section_end +``` + +### Multi-Platform CI Detection + +```bash +#!/usr/bin/env bash +set -euo pipefail + +# Detect CI environment +detect_ci() { + if [[ "${GITHUB_ACTIONS:-false}" == "true" ]]; then + echo "github" + elif [[ -n "${TF_BUILD:-}" ]]; then + echo "azuredevops" + elif [[ -n "${GITLAB_CI:-}" ]]; then + echo "gitlab" + elif [[ -n "${CIRCLECI:-}" ]]; then + echo "circleci" + elif [[ -n "${JENKINS_URL:-}" ]]; then + echo "jenkins" + else + echo "local" + fi +} + +# Universal output +ci_output() { + local name="$1" + local value="$2" + local ci_env + ci_env=$(detect_ci) + + case "$ci_env" in + github) + echo "${name}=${value}" >> "$GITHUB_OUTPUT" + ;; + azuredevops) + echo "##vso[task.setvariable variable=$name]$value" + ;; + gitlab) + echo "${name}=${value}" >> ci_output.env + ;; + *) + echo "export ${name}=\"${value}\"" + ;; + esac +} + +# Universal error +ci_error() { + local ci_env + ci_env=$(detect_ci) + + case "$ci_env" in + github) + echo "::error::$*" + ;; + azuredevops) + echo "##vso[task.logissue type=error]$*" + ;; + *) + echo "ERROR: $*" >&2 + ;; + esac +} +``` + +## Cloud Provider Patterns + +### AWS Helper Functions + +```bash +#!/usr/bin/env bash +set -euo pipefail + +# Check AWS CLI availability +require_aws() { + if ! command -v aws &> /dev/null; then + echo "Error: AWS CLI not installed" >&2 + exit 1 + fi +} + +# Get AWS account ID +get_aws_account_id() { + aws sts get-caller-identity --query Account --output text +} + +# Get secret from AWS Secrets Manager +get_aws_secret() { + local secret_name="$1" + + aws secretsmanager get-secret-value \ + --secret-id "$secret_name" \ + --query SecretString \ + --output text +} + +# Upload to S3 with retry +s3_upload_retry() { + local file="$1" + local s3_path="$2" + local max_attempts=3 + local attempt=1 + + while ((attempt <= max_attempts)); do + if aws s3 cp "$file" "$s3_path"; then + return 0 + fi + + echo "Upload failed (attempt $attempt/$max_attempts)" >&2 + ((attempt++)) + sleep $((attempt * 2)) + done + + return 1 +} + +# Assume IAM role +assume_role() { + local role_arn="$1" + local session_name="${2:-bash-script}" + + local credentials + credentials=$(aws sts assume-role \ + --role-arn "$role_arn" \ + --role-session-name "$session_name" \ + --query Credentials \ + --output json) + + export AWS_ACCESS_KEY_ID=$(echo "$credentials" | jq -r .AccessKeyId) + export AWS_SECRET_ACCESS_KEY=$(echo "$credentials" | jq -r .SecretAccessKey) + export AWS_SESSION_TOKEN=$(echo "$credentials" | jq -r .SessionToken) +} +``` + +### Azure Helper Functions + +```bash +#!/usr/bin/env bash +set -euo pipefail + +# Check Azure CLI +require_az() { + if ! command -v az &> /dev/null; then + echo "Error: Azure CLI not installed" >&2 + exit 1 + fi +} + +# Get Azure subscription ID +get_az_subscription_id() { + az account show --query id --output tsv +} + +# Get secret from Azure Key Vault +get_keyvault_secret() { + local vault_name="$1" + local secret_name="$2" + + az keyvault secret show \ + --vault-name "$vault_name" \ + --name "$secret_name" \ + --query value \ + --output tsv +} + +# Upload to Azure Blob Storage +az_blob_upload() { + local file="$1" + local container="$2" + local blob_name="${3:-$(basename "$file")}" + + az storage blob upload \ + --file "$file" \ + --container-name "$container" \ + --name "$blob_name" \ + --overwrite +} + +# Get managed identity token +get_managed_identity_token() { + local resource="${1:-https://management.azure.com/}" + + curl -sf -H Metadata:true \ + "http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=$resource" \ + | jq -r .access_token +} +``` + +## Parallel Processing Patterns + +### GNU Parallel + +```bash +#!/usr/bin/env bash +set -euo pipefail + +# Check for GNU parallel +if command -v parallel &> /dev/null; then + # Process files in parallel + export -f process_file + find data/ -name "*.json" | parallel -j 4 process_file {} +else + # Fallback to bash background jobs + max_jobs=4 + job_count=0 + + for file in data/*.json; do + process_file "$file" & + + ((job_count++)) + if ((job_count >= max_jobs)); then + wait -n + ((job_count--)) + fi + done + + wait # Wait for remaining jobs +fi +``` + +### Job Pool Pattern + +```bash +#!/usr/bin/env bash +set -euo pipefail + +# Job pool implementation +job_pool_init() { + readonly MAX_JOBS="${1:-4}" + JOB_POOL_PIDS=() +} + +job_pool_run() { + # Wait if pool is full + while ((${#JOB_POOL_PIDS[@]} >= MAX_JOBS)); do + job_pool_wait_any + done + + # Start new job + "$@" & + JOB_POOL_PIDS+=($!) +} + +job_pool_wait_any() { + # Wait for any job to finish + if ((${#JOB_POOL_PIDS[@]} > 0)); then + local pid + wait -n + # Remove finished PIDs + for i in "${!JOB_POOL_PIDS[@]}"; do + if ! kill -0 "${JOB_POOL_PIDS[$i]}" 2>/dev/null; then + unset 'JOB_POOL_PIDS[$i]' + fi + done + JOB_POOL_PIDS=("${JOB_POOL_PIDS[@]}") # Reindex + fi +} + +job_pool_wait_all() { + wait + JOB_POOL_PIDS=() +} + +# Usage +job_pool_init 4 +for file in data/*.txt; do + job_pool_run process_file "$file" +done +job_pool_wait_all +``` + +## Deployment Patterns + +### Blue-Green Deployment + +```bash +#!/usr/bin/env bash +set -euo pipefail + +# Blue-green deployment helper +deploy_blue_green() { + local new_version="$1" + local health_check_url="$2" + + echo "Starting blue-green deployment: $new_version" + + # Deploy to green (inactive) environment + echo "Deploying to green environment..." + deploy_to_environment "green" "$new_version" + + # Health check + echo "Running health checks..." + if ! check_health "$health_check_url"; then + echo "Health check failed, rolling back" >&2 + return 1 + fi + + # Switch traffic to green + echo "Switching traffic to green..." + switch_traffic "green" + + # Keep blue as backup for rollback + echo "Deployment complete. Blue environment kept for rollback." +} + +check_health() { + local url="$1" + local max_attempts=30 + local attempt=1 + + while ((attempt <= max_attempts)); do + if curl -sf "$url" > /dev/null; then + return 0 + fi + + echo "Health check attempt $attempt/$max_attempts failed" + sleep 2 + ((attempt++)) + done + + return 1 +} +``` + +### Canary Deployment + +```bash +#!/usr/bin/env bash +set -euo pipefail + +# Canary deployment with gradual rollout +deploy_canary() { + local new_version="$1" + local stages=(5 10 25 50 100) # Percentage stages + + echo "Starting canary deployment: $new_version" + + for percentage in "${stages[@]}"; do + echo "Rolling out to $percentage% of traffic..." + + # Update traffic split + update_traffic_split "$percentage" "$new_version" + + # Monitor for issues + echo "Monitoring for 5 minutes..." + if ! monitor_metrics 300; then + echo "Issues detected, rolling back" >&2 + update_traffic_split 0 "$new_version" + return 1 + fi + + echo "Stage $percentage% successful" + done + + echo "Canary deployment complete" +} + +monitor_metrics() { + local duration="$1" + local end_time=$((SECONDS + duration)) + + while ((SECONDS < end_time)); do + # Check error rate + local error_rate + error_rate=$(get_error_rate) + + if ((error_rate > 5)); then + echo "Error rate too high: $error_rate%" >&2 + return 1 + fi + + sleep 10 + done + + return 0 +} +``` + +## Logging and Monitoring + +### Structured Logging + +```bash +#!/usr/bin/env bash +set -euo pipefail + +# JSON structured logging +log_json() { + local level="$1" + local message="$2" + shift 2 + + local timestamp + timestamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ") + + # Build JSON + local json + json=$(jq -n \ + --arg timestamp "$timestamp" \ + --arg level "$level" \ + --arg message "$message" \ + --arg script "$SCRIPT_NAME" \ + --argjson context "$(echo "$@" | jq -Rs .)" \ + '{ + timestamp: $timestamp, + level: $level, + message: $message, + script: $script, + context: $context + }') + + echo "$json" >&2 +} + +log_info() { log_json "INFO" "$@"; } +log_error() { log_json "ERROR" "$@"; } +log_warn() { log_json "WARN" "$@"; } + +# Usage +log_info "Processing file" "file=/data/input.txt" "size=1024" +``` + +## Resources + +- [12-Factor App Methodology](https://12factor.net/) +- [Container Best Practices](https://cloud.google.com/architecture/best-practices-for-building-containers) +- [CI/CD Best Practices](https://www.atlassian.com/continuous-delivery/principles/continuous-integration-vs-delivery-vs-deployment) + +--- + +**Modern automation requires cloud-native patterns, container awareness, and robust CI/CD integration. These patterns ensure production-ready deployments in 2025.** diff --git a/skills/security-first-2025.md b/skills/security-first-2025.md new file mode 100644 index 0000000..1014a1f --- /dev/null +++ b/skills/security-first-2025.md @@ -0,0 +1,582 @@ +--- +name: security-first-2025 +description: Security-first bash scripting patterns for 2025 (mandatory validation, zero-trust) +--- + +## 🚨 CRITICAL GUIDELINES + +### Windows File Path Requirements + +**MANDATORY: Always Use Backslashes on Windows for File Paths** + +When using Edit or Write tools on Windows, you MUST use backslashes (`\`) in file paths, NOT forward slashes (`/`). + +**Examples:** +- ❌ WRONG: `D:/repos/project/file.tsx` +- ✅ CORRECT: `D:\repos\project\file.tsx` + +This applies to: +- Edit tool file_path parameter +- Write tool file_path parameter +- All file operations on Windows systems + + +### Documentation Guidelines + +**NEVER create new documentation files unless explicitly requested by the user.** + +- **Priority**: Update existing README.md files rather than creating new documentation +- **Repository cleanliness**: Keep repository root clean - only README.md unless user requests otherwise +- **Style**: Documentation should be concise, direct, and professional - avoid AI-generated tone +- **User preference**: Only create additional .md files when user specifically asks for documentation + + +--- + +# Security-First Bash Scripting (2025) + +## Overview + +2025 security assessments reveal **60%+ of exploited automation tools lacked adequate input sanitization**. This skill provides mandatory security patterns. + +## Critical Security Patterns + +### 1. Input Validation (Non-Negotiable) + +**Every input MUST be validated before use:** + +```bash +#!/usr/bin/env bash +set -euo pipefail + +# ✅ REQUIRED: Validate all inputs +validate_input() { + local input="$1" + local pattern="$2" + local max_length="${3:-255}" + + # Check empty + if [[ -z "$input" ]]; then + echo "Error: Input required" >&2 + return 1 + fi + + # Check pattern + if [[ ! "$input" =~ $pattern ]]; then + echo "Error: Invalid format" >&2 + return 1 + fi + + # Check length + if [[ ${#input} -gt $max_length ]]; then + echo "Error: Input too long (max $max_length)" >&2 + return 1 + fi + + return 0 +} + +# Usage +read -r user_input +if validate_input "$user_input" '^[a-zA-Z0-9_-]+$' 50; then + process "$user_input" +else + exit 1 +fi +``` + +### 2. Command Injection Prevention + +**NEVER use eval or dynamic execution with user input:** + +```bash +# ❌ DANGEROUS - Command injection vulnerability +user_input="$(cat user_file.txt)" +eval "$user_input" # NEVER DO THIS + +# ❌ DANGEROUS - Indirect command injection +grep "$user_pattern" file.txt # If pattern is "-e /etc/passwd" + +# ✅ SAFE - Use -- separator +grep -- "$user_pattern" file.txt + +# ✅ SAFE - Use arrays +grep_args=("$user_pattern" "file.txt") +grep "${grep_args[@]}" + +# ✅ SAFE - Validate before use +if [[ "$user_pattern" =~ ^[a-zA-Z0-9]+$ ]]; then + grep "$user_pattern" file.txt +fi +``` + +### 3. Path Traversal Prevention + +**Sanitize and validate ALL file paths:** + +```bash +#!/usr/bin/env bash +set -euo pipefail + +# Sanitize path components +sanitize_path() { + local path="$1" + + # Remove dangerous patterns + path="${path//..\/}" # Remove ../ + path="${path//\/..\//}" # Remove /../ + path="${path#/}" # Remove leading / + + echo "$path" +} + +# Validate path is within allowed directory +is_safe_path() { + local file_path="$1" + local base_dir="$2" + + # Resolve to absolute paths + local real_path real_base + real_path=$(readlink -f "$file_path" 2>/dev/null) || return 1 + real_base=$(readlink -f "$base_dir" 2>/dev/null) || return 1 + + # Check path starts with base + [[ "$real_path" == "$real_base"/* ]] +} + +# Usage +user_file=$(sanitize_path "$user_input") +if is_safe_path "/var/app/uploads/$user_file" "/var/app/uploads"; then + cat "/var/app/uploads/$user_file" +else + echo "Error: Access denied" >&2 + exit 1 +fi +``` + +### 4. Secure Temporary Files + +**Never use predictable temp file names:** + +```bash +# ❌ DANGEROUS - Race condition vulnerability +temp_file="/tmp/myapp.tmp" +echo "data" > "$temp_file" # Can be symlinked by attacker + +# ❌ DANGEROUS - Predictable name +temp_file="/tmp/myapp-$$.tmp" # PID can be guessed + +# ✅ SAFE - Use mktemp +temp_file=$(mktemp) +chmod 600 "$temp_file" # Owner-only permissions +echo "data" > "$temp_file" + +# ✅ SAFE - Automatic cleanup +readonly TEMP_FILE=$(mktemp) +trap 'rm -f "$TEMP_FILE"' EXIT INT TERM + +# ✅ SAFE - Temp directory +readonly TEMP_DIR=$(mktemp -d) +trap 'rm -rf "$TEMP_DIR"' EXIT INT TERM +chmod 700 "$TEMP_DIR" +``` + +### 5. Secrets Management + +**NEVER hardcode secrets or expose them:** + +```bash +# ❌ DANGEROUS - Hardcoded secrets +DB_PASSWORD="supersecret123" + +# ❌ DANGEROUS - Secrets in environment (visible in ps) +export DB_PASSWORD="supersecret123" + +# ✅ SAFE - Read from secure file +if [[ -f /run/secrets/db_password ]]; then + DB_PASSWORD=$(< /run/secrets/db_password) + chmod 600 /run/secrets/db_password +else + echo "Error: Secret not found" >&2 + exit 1 +fi + +# ✅ SAFE - Use cloud secret managers +get_secret() { + local secret_name="$1" + + # AWS Secrets Manager + aws secretsmanager get-secret-value \ + --secret-id "$secret_name" \ + --query SecretString \ + --output text +} + +DB_PASSWORD=$(get_secret "production/database/password") + +# ✅ SAFE - Prompt for sensitive data (no echo) +read -rsp "Enter password: " password +echo # Newline after password +``` + +### 6. Privilege Management + +**Follow least privilege principle:** + +```bash +#!/usr/bin/env bash +set -euo pipefail + +# Check not running as root +if [[ $EUID -eq 0 ]]; then + echo "Error: Do not run as root" >&2 + exit 1 +fi + +# Drop privileges if started as root +drop_privileges() { + local target_user="$1" + + if [[ $EUID -eq 0 ]]; then + echo "Dropping privileges to $target_user" >&2 + exec sudo -u "$target_user" "$0" "$@" + fi +} + +# Run specific command with minimal privileges +run_privileged() { + local command="$1" + shift + + # Use sudo with minimal scope + sudo --non-interactive \ + --reset-timestamp \ + "$command" "$@" +} + +# Usage +drop_privileges "appuser" +``` + +### 7. Environment Variable Sanitization + +**Clean environment before executing:** + +```bash +#!/usr/bin/env bash +set -euo pipefail + +# Clean environment +clean_environment() { + # Unset dangerous variables + unset IFS + unset CDPATH + unset GLOBIGNORE + + # Set safe PATH (absolute paths only) + export PATH="/usr/local/bin:/usr/bin:/bin" + + # Set safe IFS + IFS=$'\n\t' +} + +# Execute command in clean environment +exec_clean() { + env -i \ + HOME="$HOME" \ + USER="$USER" \ + PATH="/usr/local/bin:/usr/bin:/bin" \ + "$@" +} + +# Usage +clean_environment +exec_clean /usr/local/bin/myapp +``` + +### 8. Absolute Path Usage (2025 Best Practice) + +**Always use absolute paths to prevent PATH hijacking:** + +```bash +#!/usr/bin/env bash +set -euo pipefail + +# ❌ DANGEROUS - Vulnerable to PATH manipulation +curl https://example.com/data +jq '.items[]' data.json + +# ✅ SAFE - Absolute paths +/usr/bin/curl https://example.com/data +/usr/bin/jq '.items[]' data.json + +# ✅ SAFE - Verify command location +CURL=$(command -v curl) || { echo "curl not found" >&2; exit 1; } +"$CURL" https://example.com/data +``` + +**Why This Matters:** +- Prevents malicious binaries in user PATH +- Standard practice in enterprise environments +- Required for security-sensitive scripts + +### 9. History File Protection (2025 Security) + +**Disable history for credential operations:** + +```bash +#!/usr/bin/env bash +set -euo pipefail + +# Disable history for this session +HISTFILE=/dev/null +export HISTFILE + +# Or disable specific commands +HISTIGNORE="*password*:*secret*:*token*" +export HISTIGNORE + +# Handle sensitive operations +read -rsp "Enter database password: " db_password +echo + +# Use password (not logged to history) +/usr/bin/mysql -p"$db_password" -e "SELECT 1" + +# Clear variable +unset db_password +``` + +## Security Checklist (2025) + +Every script MUST pass these checks: + +### Input Validation +- [ ] All user inputs validated with regex patterns +- [ ] Maximum length enforced on all inputs +- [ ] Empty/null inputs rejected +- [ ] Special characters escaped or rejected + +### Command Safety +- [ ] No eval with user input +- [ ] No dynamic variable names from user input +- [ ] All command arguments use -- separator +- [ ] Arrays used instead of string concatenation + +### File Operations +- [ ] All paths validated against directory traversal +- [ ] Temp files created with mktemp +- [ ] File permissions set restrictively (600/700) +- [ ] Cleanup handlers registered (trap EXIT) + +### Secrets +- [ ] No hardcoded passwords/keys/tokens +- [ ] Secrets read from secure storage +- [ ] Secrets never logged or printed +- [ ] Secrets cleared from memory when done + +### Privileges +- [ ] Runs with minimum required privileges +- [ ] Root execution rejected unless necessary +- [ ] Privilege drops implemented where needed +- [ ] Sudo scope minimized + +### Error Handling +- [ ] set -euo pipefail enabled +- [ ] All errors logged to stderr +- [ ] Sensitive data not exposed in errors +- [ ] Exit codes meaningful + +## Automated Security Scanning + +### ShellCheck Integration + +```bash +# .github/workflows/security.yml +name: Security Scan + +on: [push, pull_request] + +jobs: + shellcheck: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: ShellCheck + run: | + # Fail on security issues + find . -name "*.sh" -exec shellcheck \ + --severity=error \ + --enable=all \ + {} + +``` + +### Custom Security Linting + +```bash +#!/usr/bin/env bash +# security-lint.sh - Check scripts for security issues + +set -euo pipefail + +lint_script() { + local script="$1" + local issues=0 + + echo "Checking: $script" + + # Check for eval + if grep -n "eval" "$script"; then + echo " ❌ Found eval (command injection risk)" + ((issues++)) + fi + + # Check for hardcoded secrets + if grep -nE "(password|secret|token|key)\s*=\s*['\"][^'\"]+['\"]" "$script"; then + echo " ❌ Found hardcoded secrets" + ((issues++)) + fi + + # Check for predictable temp files + if grep -n "/tmp/[a-zA-Z0-9_-]*\\.tmp" "$script"; then + echo " ❌ Found predictable temp file" + ((issues++)) + fi + + # Check for unquoted variables + if grep -nE '\$[A-Z_]+[^"]' "$script"; then + echo " ⚠️ Found unquoted variables" + ((issues++)) + fi + + if ((issues == 0)); then + echo " ✓ No security issues found" + fi + + return "$issues" +} + +# Scan all scripts +total_issues=0 +while IFS= read -r -d '' script; do + lint_script "$script" || ((total_issues++)) +done < <(find . -name "*.sh" -type f -print0) + +if ((total_issues > 0)); then + echo "❌ Found security issues in $total_issues scripts" + exit 1 +else + echo "✓ All scripts passed security checks" +fi +``` + +## Real-World Secure Script Template + +```bash +#!/usr/bin/env bash +# +# Secure Script Template (2025) +# + +set -euo pipefail +IFS=$'\n\t' + +readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +readonly SCRIPT_NAME="$(basename "${BASH_SOURCE[0]}")" + +# Security: Reject root execution +if [[ $EUID -eq 0 ]]; then + echo "Error: Do not run as root" >&2 + exit 1 +fi + +# Security: Clean environment +export PATH="/usr/local/bin:/usr/bin:/bin" +unset CDPATH GLOBIGNORE + +# Security: Secure temp file +readonly TEMP_FILE=$(mktemp) +trap 'rm -f "$TEMP_FILE"; exit' EXIT INT TERM +chmod 600 "$TEMP_FILE" + +# Validate input +validate_input() { + local input="$1" + + if [[ -z "$input" ]]; then + echo "Error: Input required" >&2 + return 1 + fi + + if [[ ! "$input" =~ ^[a-zA-Z0-9_/-]+$ ]]; then + echo "Error: Invalid characters in input" >&2 + return 1 + fi + + if [[ ${#input} -gt 255 ]]; then + echo "Error: Input too long" >&2 + return 1 + fi + + return 0 +} + +# Sanitize file path +sanitize_path() { + local path="$1" + path="${path//..\/}" + path="${path#/}" + echo "$path" +} + +# Main function +main() { + local user_input="${1:-}" + + # Validate + if ! validate_input "$user_input"; then + exit 1 + fi + + # Sanitize + local safe_path + safe_path=$(sanitize_path "$user_input") + + # Process safely + echo "Processing: $safe_path" + # ... your logic here ... +} + +main "$@" +``` + +## Compliance Standards (2025) + +### CIS Benchmarks +- Use ShellCheck for automated compliance +- Implement input validation on all user data +- Secure temporary file handling +- Least privilege execution + +### NIST Guidelines +- Strong input validation (NIST SP 800-53) +- Secure coding practices +- Logging and monitoring +- Access control enforcement + +### OWASP Top 10 +- A03: Injection - Prevent command injection +- A01: Broken Access Control - Path validation +- A02: Cryptographic Failures - Secure secrets + +## Resources + +- [CIS Docker Benchmark](https://www.cisecurity.org/benchmark/docker) +- [OWASP Command Injection](https://owasp.org/www-community/attacks/Command_Injection) +- [ShellCheck Security Rules](https://www.shellcheck.net/wiki/) +- [NIST SP 800-53](https://csrc.nist.gov/publications/detail/sp/800-53/rev-5/final) + +--- + +**Security-first development is non-negotiable in 2025. Every script must pass all security checks before deployment.** diff --git a/skills/shellcheck-cicd-2025.md b/skills/shellcheck-cicd-2025.md new file mode 100644 index 0000000..8a82c35 --- /dev/null +++ b/skills/shellcheck-cicd-2025.md @@ -0,0 +1,441 @@ +--- +name: shellcheck-cicd-2025 +description: ShellCheck validation as non-negotiable 2025 workflow practice +--- + +## 🚨 CRITICAL GUIDELINES + +### Windows File Path Requirements + +**MANDATORY: Always Use Backslashes on Windows for File Paths** + +When using Edit or Write tools on Windows, you MUST use backslashes (`\`) in file paths, NOT forward slashes (`/`). + +**Examples:** +- ❌ WRONG: `D:/repos/project/file.tsx` +- ✅ CORRECT: `D:\repos\project\file.tsx` + +This applies to: +- Edit tool file_path parameter +- Write tool file_path parameter +- All file operations on Windows systems + + +### Documentation Guidelines + +**NEVER create new documentation files unless explicitly requested by the user.** + +- **Priority**: Update existing README.md files rather than creating new documentation +- **Repository cleanliness**: Keep repository root clean - only README.md unless user requests otherwise +- **Style**: Documentation should be concise, direct, and professional - avoid AI-generated tone +- **User preference**: Only create additional .md files when user specifically asks for documentation + + +--- + +# ShellCheck CI/CD Integration (2025) + +## ShellCheck: Non-Negotiable in 2025 + +ShellCheck is now considered **mandatory** in modern bash workflows (2025 best practices): + +### Latest Version: v0.11.0 (August 2025) + +**What's New:** +- Full Bash 5.3 support (`${| cmd; }` and `source -p`) +- **New warnings**: SC2327/SC2328 (capture group issues) +- **POSIX.1-2024 compliance**: SC3013 removed (-ot/-nt/-ef now POSIX standard) +- Enhanced static analysis capabilities +- Improved performance and accuracy + +### Why Mandatory? + +- Catches subtle bugs before production +- Prevents common security vulnerabilities +- Enforces consistent code quality +- Required by most DevOps teams +- Standard in enterprise environments +- Supports latest POSIX.1-2024 standard + +## Installation + +```bash +# Ubuntu/Debian +apt-get install shellcheck + +# macOS +brew install shellcheck + +# Alpine (Docker) +apk add shellcheck + +# Windows (WSL/Git Bash) +choco install shellcheck + +# Or download binary +wget https://github.com/koalaman/shellcheck/releases/latest/download/shellcheck-stable.linux.x86_64.tar.xz +tar -xf shellcheck-stable.linux.x86_64.tar.xz +sudo cp shellcheck-stable/shellcheck /usr/local/bin/ +``` + +## GitHub Actions Integration + +### Mandatory Pre-Merge Check + +```yaml +# .github/workflows/shellcheck.yml +name: ShellCheck + +on: + pull_request: + paths: + - '**.sh' + - '**Dockerfile' + push: + branches: [main] + +jobs: + shellcheck: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: Run ShellCheck + uses: ludeeus/action-shellcheck@master + with: + severity: warning + format: gcc # or: tty, json, checkstyle + scandir: './scripts' + # Fail on any issues + ignore_paths: 'node_modules' + + # Block merge on failures + - name: Annotate PR + if: failure() + uses: actions/github-script@v6 + with: + script: | + github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: '⛔ ShellCheck validation failed. Fix issues before merging.' + }) +``` + +## Azure DevOps Integration + +```yaml +# azure-pipelines.yml +trigger: +- main + +pr: +- main + +stages: +- stage: Validate + jobs: + - job: ShellCheck + pool: + vmImage: 'ubuntu-24.04' + + steps: + - script: | + sudo apt-get install -y shellcheck + displayName: 'Install ShellCheck' + + - script: | + find . -name "*.sh" -type f | xargs shellcheck --format=gcc --severity=warning + displayName: 'Run ShellCheck' + failOnStderr: true + + - task: PublishTestResults@2 + condition: always() + inputs: + testResultsFormat: 'JUnit' + testResultsFiles: '**/shellcheck-results.xml' + failTaskOnFailedTests: true +``` + +## Git Hooks (Pre-Commit) + +```bash +# .git/hooks/pre-commit +#!/usr/bin/env bash +set -o errexit +set -o nounset +set -o pipefail + +# Find all staged .sh files +mapfile -t STAGED_SH < <(git diff --cached --name-only --diff-filter=ACMR | grep '\.sh$' || true) + +if [ ${#STAGED_SH[@]} -eq 0 ]; then + exit 0 +fi + +echo "Running ShellCheck on staged files..." + +# Run ShellCheck +shellcheck --format=gcc --severity=warning "${STAGED_SH[@]}" + +if [ $? -ne 0 ]; then + echo "⛔ ShellCheck failed. Fix issues before committing." + exit 1 +fi + +echo "✅ ShellCheck passed" +exit 0 +``` + +**Install Pre-Commit Hook:** +```bash +chmod +x .git/hooks/pre-commit + +# Or use pre-commit framework +# .pre-commit-config.yaml +repos: +- repo: https://github.com/shellcheck-py/shellcheck-py + rev: v0.11.0.0 + hooks: + - id: shellcheck + args: ['--severity=warning'] + +# Install +pip install pre-commit +pre-commit install +``` + +## VS Code Integration + +```json +// .vscode/settings.json +{ + "shellcheck.enable": true, + "shellcheck.run": "onType", + "shellcheck.executablePath": "/usr/local/bin/shellcheck", + "shellcheck.exclude": ["SC1090", "SC1091"], // Optional excludes + "shellcheck.customArgs": [ + "-x", // Follow source files + "--severity=warning" + ] +} +``` + +## Docker Build Integration + +```dockerfile +# Dockerfile with ShellCheck validation +FROM alpine:3.19 AS builder + +# Install ShellCheck +RUN apk add --no-cache shellcheck bash + +# Copy scripts +COPY scripts/ /scripts/ + +# Validate all scripts before continuing +RUN find /scripts -name "*.sh" -type f -exec shellcheck --severity=warning {} + + +# Final stage +FROM alpine:3.19 +COPY --from=builder /scripts/ /scripts/ +RUN chmod +x /scripts/*.sh + +ENTRYPOINT ["/scripts/entrypoint.sh"] +``` + +## Common ShellCheck Rules (2025) + +### New in v0.11.0: SC2327/SC2328 - Capture Groups + +```bash +# ❌ Bad - Capture groups may not work as expected +if [[ "$string" =~ ([0-9]+)\.([0-9]+) ]]; then + echo "$1" # Wrong: $1 is script arg, not capture group +fi + +# ✅ Good - Use BASH_REMATCH array +if [[ "$string" =~ ([0-9]+)\.([0-9]+) ]]; then + echo "${BASH_REMATCH[1]}.${BASH_REMATCH[2]}" +fi +``` + +### SC2294: eval Negates Array Benefits (New) + +```bash +# ❌ Bad - eval defeats array safety +eval "command ${array[@]}" + +# ✅ Good - Direct array usage +command "${array[@]}" +``` + +### SC2295: Quote Expansions Inside ${} + +```bash +# ❌ Bad +echo "${var-$default}" # $default not quoted + +# ✅ Good +echo "${var-"$default"}" +``` + +### SC2086: Quote Variables + +```bash +# ❌ Bad +file=$1 +cat $file # Fails if filename has spaces + +# ✅ Good +file=$1 +cat "$file" +``` + +### SC2046: Quote Command Substitution + +```bash +# ❌ Bad +for file in $(find . -name "*.txt"); do + echo $file +done + +# ✅ Good +find . -name "*.txt" -print0 | while IFS= read -r -d '' file; do + echo "$file" +done +``` + +### SC2155: Separate Declaration and Assignment + +```bash +# ❌ Bad +local result=$(command) # Hides command exit code + +# ✅ Good +local result +result=$(command) +``` + +### SC2164: Use cd || exit + +```bash +# ❌ Bad +cd /some/directory +./script.sh # Runs in wrong dir if cd fails + +# ✅ Good +cd /some/directory || exit 1 +./script.sh +``` + +## Google Shell Style Guide (50-Line Limit) + +2025 recommendation: Keep scripts under 50 lines: + +```bash +# ❌ Bad: 500-line monolithic script +#!/usr/bin/env bash +# ... 500 lines of code ... + +# ✅ Good: Modular scripts < 50 lines each + +# lib/logging.sh (20 lines) +log_info() { echo "[INFO] $*"; } +log_error() { echo "[ERROR] $*" >&2; } + +# lib/validation.sh (30 lines) +validate_input() { ... } +check_dependencies() { ... } + +# main.sh (40 lines) +source "$(dirname "$0")/lib/logging.sh" +source "$(dirname "$0")/lib/validation.sh" + +main() { + validate_input "$@" + check_dependencies + # ... core logic ... +} + +main "$@" +``` + +## Enforce in CI/CD + +### Fail Build on Issues + +```yaml +# Strict enforcement +- name: ShellCheck (Strict) + run: | + shellcheck --severity=warning scripts/*.sh + # Exit code 1 fails the build + +# Advisory only (warnings but don't fail) +- name: ShellCheck (Advisory) + run: | + shellcheck --severity=warning scripts/*.sh || true + # Logs warnings but doesn't fail +``` + +### Generate Reports + +```bash +# JSON format for parsing +shellcheck --format=json scripts/*.sh > shellcheck-report.json + +# GitHub annotations format +shellcheck --format=gcc scripts/*.sh + +# Human-readable +shellcheck --format=tty scripts/*.sh +``` + +## Modern Error Handling Trio (2025) + +Always use with ShellCheck validation: + +```bash +#!/usr/bin/env bash + +# Modern error handling (non-negotiable in 2025) +set -o errexit # Exit on command failure +set -o nounset # Exit on undefined variable +set -o pipefail # Exit on pipe failure + +# ShellCheck approved +main() { + local config_file="${1:?Config file required}" + + if [[ ! -f "$config_file" ]]; then + echo "Error: Config file not found: $config_file" >&2 + return 1 + fi + + # Safe command execution + local result + result=$(process_config "$config_file") + + echo "$result" +} + +main "$@" +``` + +## Best Practices (2025) + +1. **Run ShellCheck in CI/CD (mandatory)** +2. **Use pre-commit hooks** to catch issues early +3. **Keep scripts under 50 lines** (Google Style Guide) +4. **Use modern error handling trio** (errexit, nounset, pipefail) +5. **Fix all warnings** before merging +6. **Document any disabled rules** with reasoning +7. **Integrate with IDE** for real-time feedback + +## Resources + +- [ShellCheck](https://www.shellcheck.net) +- [Google Shell Style Guide](https://google.github.io/styleguide/shellguide.html) +- [ShellCheck GitHub Action](https://github.com/ludeeus/action-shellcheck)