Bash Script Validator Overview This skill validates Bash and POSIX shell scripts with layered checks: 1. Syntax validation ( or ) 2. ShellCheck static analysis (system binary or wrapper fallback) 3. Custom security, portability, and optimization checks Use the default flow below, then branch to fallbacks only when the environment is constrained. Trigger Guidance Use this skill when the request includes script quality, linting, syntax checking, or shell portability work. Trigger Phrases - "Validate this bash script" - "Lint this file" - "Find security issues in this shell script" - "Why does t…

\\n\\t'\n```\n\n### 3. Quote Variables\n```bash\n# Good\ncp \"$source\" \"$destination\"\n\n# Bad (fails with spaces)\ncp $source $destination\n```\n\n### 4. Use $() Instead of Backticks\n```bash\n# Good\nresult=$(command)\n\n# Bad\nresult=`command`\n```\n\n### 5. Check Command Existence\n```bash\nif command -v shellcheck &>/dev/null; then\n echo \"ShellCheck is installed\"\nfi\n```\n\n### 6. Use [[ ]] for Tests\n```bash\n# Preferred in bash\nif [[ \"$var\" == \"value\" ]]; then\n # ...\nfi\n\n# Use [ ] only for POSIX compliance\nif [ \"$var\" = \"value\" ]; then\n # ...\nfi\n```\n\n### 7. Handle Errors Appropriately\n```bash\nif ! command; then\n echo \"Command failed\" >&2\n exit 1\nfi\n\ncommand || { echo \"Failed\" >&2; exit 1; }\n```\n\n### 8. Use Meaningful Variable Names\n```bash\n# Good\nuser_count=10\nmax_retries=3\n\n# Bad\nn=10\nx=3\n```\n\n### 9. Add Comments\n```bash\n# Explain complex logic\n# Document function parameters\n# Clarify non-obvious behavior\n```\n\n### 10. Use Functions for Reusability\n```bash\nlog_error() {\n echo \"[ERROR] $*\" >&2\n}\n\nlog_info() {\n echo \"[INFO] $*\"\n}\n```\n\n## Common Pitfalls\n\n### 1. Unquoted Variables\n```bash\n# Wrong\nfile=/path/with spaces/file.txt\ncat $file # Fails!\n\n# Right\nfile=\"/path/with spaces/file.txt\"\ncat \"$file\"\n```\n\n### 2. Not Checking Return Codes\n```bash\n# Wrong\ncd /some/directory\nrm -rf * # Dangerous if cd fails!\n\n# Right\ncd /some/directory || exit 1\nrm -rf *\n```\n\n### 3. Using [ ] with Bash Features\n```bash\n# Wrong\nif [ \"$var\" == pattern* ]; then # == not in POSIX\n\n# Right (bash)\nif [[ \"$var\" == pattern* ]]; then\n\n# Right (POSIX)\nif [ \"$var\" = \"exact\" ]; then\n```\n\n### 4. Word Splitting Issues\n```bash\n# Wrong\nfiles=$(ls *.txt)\nfor file in $files; do # Breaks on spaces\n echo \"$file\"\ndone\n\n# Right\nfor file in *.txt; do\n echo \"$file\"\ndone\n```\n\n### 5. Not Using Local in Functions\n```bash\n# Wrong - pollutes global scope\nfunction bad {\n var=\"value\"\n}\n\n# Right\nfunction good {\n local var=\"value\"\n}\n```\n\n## Parameter Expansion Reference\n\n```bash\n${var} # Value of var\n${var:-default} # Use default if var is unset\n${var:=default} # Assign default if var is unset\n${var:?error} # Error if var is unset\n${var:+alternate} # Use alternate if var is set\n${#var} # Length of var\n${var:offset:length} # Substring\n${var#pattern} # Remove shortest match from beginning\n${var##pattern} # Remove longest match from beginning\n${var%pattern} # Remove shortest match from end\n${var%%pattern} # Remove longest match from end\n${var/pattern/replacement} # Replace first match\n${var//pattern/replacement} # Replace all matches\n${var^} # Uppercase first character\n${var^^} # Uppercase all\n${var,} # Lowercase first character\n${var,,} # Lowercase all\n```\n\n## Special Variables\n\n```bash\n$0 # Script name\n$1-$9 # Positional parameters\n${10} # 10th parameter (braces required)\n$# # Number of positional parameters\n$* # All positional parameters (as single word)\n$@ # All positional parameters (as separate words)\n$ # Process ID of shell\n$! # PID of last background command\n$? # Exit status of last command\n$_ # Last argument of previous command\n```\n\n## Resources\n\n- [Official GNU Bash Manual](https://www.gnu.org/software/bash/manual/)\n- [Bash Guide for Beginners](http://tldp.org/LDP/Bash-Beginners-Guide/html/)\n- [Advanced Bash-Scripting Guide](https://tldp.org/LDP/abs/html/)\n- [ShellCheck](https://www.shellcheck.net/) - Linting tool","content_type":"text/markdown; charset=utf-8","language":"markdown","size":8398,"content_sha256":"890303157c1c5961d086d3d8a8a0b98290b98a7de96a8024f8a6cf29ce6bd36f"},{"filename":"docs/common-mistakes.md","content":"# Common Shell Scripting Mistakes\n\nThis guide covers frequent mistakes made in bash and shell scripts, their consequences, and how to fix them.\n\n## 1. Unquoted Variables\n\n### Problem\n```bash\n# Wrong\nfile=/path/with spaces/file.txt\ncat $file # Breaks into multiple arguments\n```\n\n### Consequence\nWord splitting and glob expansion cause unexpected behavior.\n\n### Solution\n```bash\n# Right\nfile=\"/path/with spaces/file.txt\"\ncat \"$file\"\n```\n\n### Rule\n**Always quote variable expansions** unless you explicitly need word splitting.\n\n---\n\n## 2. Not Checking Command Success\n\n### Problem\n```bash\n# Wrong\ncd /some/directory\nrm -rf * # DANGEROUS if cd fails!\n```\n\n### Consequence\nCommands execute even if previous commands fail, potentially catastrophic.\n\n### Solution\n```bash\n# Right\ncd /some/directory || exit 1\nrm -rf *\n\n# Or use set -e\nset -e\ncd /some/directory\nrm -rf *\n\n# Or check explicitly\nif ! cd /some/directory; then\n echo \"Failed to change directory\" >&2\n exit 1\nfi\nrm -rf *\n```\n\n---\n\n## 3. Using [ ] with Bash Features\n\n### Problem\n```bash\n# Wrong (== not POSIX, may fail in sh)\nif [ \"$var\" == \"value\" ]; then\n echo \"match\"\nfi\n```\n\n### Solution\n```bash\n# POSIX sh - use single =\nif [ \"$var\" = \"value\" ]; then\n echo \"match\"\nfi\n\n# Or use bash [[ ]] (bash only)\nif [[ \"$var\" == \"value\" ]]; then\n echo \"match\"\nfi\n```\n\n---\n\n## 4. Useless Use of cat (UUOC)\n\n### Problem\n```bash\n# Wrong - unnecessary cat\ncat file.txt | grep pattern\ncat file.txt | awk '{print $1}'\n```\n\n### Consequence\nWastes a process, less efficient.\n\n### Solution\n```bash\n# Right\ngrep pattern file.txt\nawk '{print $1}' file.txt\n\n# Or use redirection\n\u003c file.txt grep pattern\n```\n\n---\n\n## 5. Not Using -r with read\n\n### Problem\n```bash\n# Wrong\nwhile read line; do\n echo \"$line\"\ndone \u003c file\n```\n\n### Consequence\nBackslashes are interpreted, leading character may be removed.\n\n### Solution\n```bash\n# Right\nwhile IFS= read -r line; do\n echo \"$line\"\ndone \u003c file\n```\n\n- `-r` prevents backslash interpretation\n- `IFS=` prevents leading/trailing whitespace trimming\n\n---\n\n## 6. Testing $? After Multiple Commands\n\n### Problem\n```bash\n# Wrong\ncommand1\ncommand2\nif [ $? -eq 0 ]; then # Tests command2, not command1!\n echo \"Success\"\nfi\n```\n\n### Solution\n```bash\n# Right - test immediately\ncommand1\nif [ $? -eq 0 ]; then\n echo \"command1 succeeded\"\nfi\n\n# Better - test directly\nif command1; then\n echo \"Success\"\nfi\n```\n\n---\n\n## 7. Arrays in POSIX sh Scripts\n\n### Problem\n```bash\n#!/bin/sh\n# Wrong - arrays not in POSIX sh\narray=(one two three)\necho \"${array[0]}\"\n```\n\n### Solution\n```bash\n# Use bash\n#!/bin/bash\narray=(one two three)\necho \"${array[0]}\"\n\n# Or use POSIX alternatives\nset -- one two three\necho \"$1\"\n```\n\n---\n\n## 8. Not Declaring Functions Before Use\n\n### Problem\n```bash\n# Wrong - function not defined yet\nmy_function\n\nmy_function() {\n echo \"Hello\"\n}\n```\n\n### Solution\n```bash\n# Right - define first\nmy_function() {\n echo \"Hello\"\n}\n\nmy_function\n```\n\n---\n\n## 9. Using eval Unsafely\n\n### Problem\n```bash\n# DANGEROUS\nuser_input=\"$1\"\neval \"$user_input\" # Command injection risk!\n```\n\n### Consequence\nSecurity vulnerability - arbitrary code execution.\n\n### Solution\n```bash\n# Avoid eval when possible\n# If necessary, sanitize input thoroughly\n# Or use safer alternatives\n\n# Example: dynamic variable names\nvar_name=\"my_var\"\n# Don't: eval \"echo \\$var_name\"\n# Do: Use indirect expansion (bash)\necho \"${!var_name}\"\n```\n\n---\n\n## 10. Forgetting set -u\n\n### Problem\n```bash\n# Wrong - typo goes unnoticed\nnmae=\"John\" # Typo\necho \"Hello, $name\" # Prints \"Hello, \" (empty)\n```\n\n### Solution\n```bash\n# Right - use set -u\nset -u\nnmae=\"John\" # Typo\necho \"Hello, $name\" # Error: name: unbound variable\n```\n\n---\n\n## 11. Incorrect String Comparison\n\n### Problem\n```bash\n# Wrong - numeric comparison on strings\nif [ \"$version\" -gt \"2.0\" ]; then\n echo \"New version\"\nfi\n```\n\n### Solution\n```bash\n# Right - string comparison\nif [ \"$version\" = \"2.0\" ]; then\n echo \"Exact match\"\nfi\n\n# Or use proper version comparison\nif [[ \"$version\" > \"2.0\" ]]; then\n echo \"Greater\"\nfi\n```\n\n---\n\n## 12. Not Handling Spaces in Filenames\n\n### Problem\n```bash\n# Wrong\nfor file in $(ls *.txt); do\n echo \"$file\"\ndone\n```\n\n### Consequence\nFiles with spaces break into multiple items.\n\n### Solution\n```bash\n# Right - use glob directly\nfor file in *.txt; do\n echo \"$file\"\ndone\n\n# Or use find with -print0\nwhile IFS= read -r -d '' file; do\n echo \"$file\"\ndone \u003c \u003c(find . -name \"*.txt\" -print0)\n```\n\n---\n\n## 13. Backticks Instead of $()\n\n### Problem\n```bash\n# Deprecated\nresult=`command arg1 arg2`\n```\n\n### Solution\n```bash\n# Modern\nresult=$(command arg1 arg2)\n```\n\n### Why\n- Better nesting: `$(cmd1 $(cmd2))`\n- Better readability\n- Fewer escaping issues\n\n---\n\n## 14. Using = Instead of == in [[ ]]\n\nNot really a mistake, but inconsistent:\n\n```bash\n# Both work in [[ ]]\n[[ \"$var\" = \"value\" ]] # POSIX style (works)\n[[ \"$var\" == \"value\" ]] # Bash style (also works)\n\n# Only = works in [ ]\n[ \"$var\" = \"value\" ] # Works\n[ \"$var\" == \"value\" ] # May fail in POSIX sh\n```\n\n**Recommendation**: Use `=` for portability, or stick to `==` in bash with `[[ ]]`.\n\n---\n\n## 15. Not Quoting $@\n\n### Problem\n```bash\n# Wrong\nscript.sh \"$@\" # Right\ncommand $@ # Wrong if args have spaces\n```\n\n### Solution\n```bash\n# Right\ncommand \"$@\" # Preserves argument boundaries\n```\n\n---\n\n## 16. Using ls to Process Files\n\n### Problem\n```bash\n# Wrong\nfiles=$(ls *.txt)\nfor file in $files; do\n process \"$file\"\ndone\n```\n\n### Issues\n- Breaks on spaces\n- Breaks on newlines in filenames\n- Breaks on glob characters\n\n### Solution\n```bash\n# Right\nfor file in *.txt; do\n process \"$file\"\ndone\n\n# Or with find\nfind . -name \"*.txt\" -exec process {} \\;\n```\n\n---\n\n## 17. Incorrect Exit Codes\n\n### Problem\n```bash\n# Wrong\nfunction check_file() {\n if [ -f \"$1\" ]; then\n echo \"File exists\"\n return 1 # Success should be 0!\n fi\n return 0\n}\n```\n\n### Solution\n```bash\n# Right - 0 is success, non-zero is failure\nfunction check_file() {\n if [ -f \"$1\" ]; then\n echo \"File exists\"\n return 0\n fi\n return 1\n}\n```\n\n---\n\n## 18. Using -a and -o in [ ]\n\n### Problem\n```bash\n# Deprecated and error-prone\n[ \"$a\" = \"x\" -a \"$b\" = \"y\" ]\n[ \"$a\" = \"x\" -o \"$b\" = \"y\" ]\n```\n\n### Solution\n```bash\n# Right - use && and ||\n[ \"$a\" = \"x\" ] && [ \"$b\" = \"y\" ]\n[ \"$a\" = \"x\" ] || [ \"$b\" = \"y\" ]\n\n# Or use [[ ]] in bash\n[[ \"$a\" = \"x\" && \"$b\" = \"y\" ]]\n[[ \"$a\" = \"x\" || \"$b\" = \"y\" ]]\n```\n\n---\n\n## 19. Not Making Scripts Executable\n\n### Problem\n```bash\n# Wrong\nbash script.sh # Works but not ideal\n```\n\n### Solution\n```bash\n# Right\nchmod +x script.sh\n./script.sh\n```\n\nAnd include proper shebang:\n```bash\n#!/usr/bin/env bash\n```\n\n---\n\n## 20. Forgetting Final Newline\n\n### Problem\nSome tools expect files to end with a newline.\n\n### Solution\nEnsure your editor adds a final newline, or:\n```bash\necho \"\" >> file\n```\n\n---\n\n## 21. Using grep -q Without Knowing Implications\n\n### Problem\n```bash\n# Potentially inefficient\nif [ \"$(grep pattern file)\" ]; then\n echo \"Found\"\nfi\n```\n\n### Solution\n```bash\n# Better - grep -q exits on first match\nif grep -q pattern file; then\n echo \"Found\"\nfi\n```\n\n---\n\n## 22. Incorrect glob Pattern\n\n### Problem\n```bash\n# Wrong - doesn't match hidden files\nfor file in *; do\n process \"$file\"\ndone\n```\n\n### Solution\n```bash\n# Include hidden files (bash)\nshopt -s dotglob\nfor file in *; do\n process \"$file\"\ndone\nshopt -u dotglob\n\n# Or explicitly\nfor file in * .[!.]* ..?*; do\n [ -e \"$file\" ] && process \"$file\"\ndone\n```\n\n---\n\n## 23. Not Handling Empty Globs\n\n### Problem\n```bash\n# Fails if no .txt files\nfor file in *.txt; do\n process \"$file\" # Processes literal \"*.txt\"\ndone\n```\n\n### Solution\n```bash\n# Bash - fail gracefully\nshopt -s nullglob\nfor file in *.txt; do\n process \"$file\"\ndone\nshopt -u nullglob\n\n# POSIX - check existence\nfor file in *.txt; do\n [ -e \"$file\" ] || continue\n process \"$file\"\ndone\n```\n\n---\n\n## 24. Not Sanitizing Input\n\n### Problem\n```bash\n# Dangerous\nrm -rf \"/$1\" # What if $1 is empty or manipulated?\n```\n\n### Solution\n```bash\n# Safer\nif [ -z \"$1\" ]; then\n echo \"Error: No argument provided\" >&2\n exit 1\nfi\n\n# Validate\ncase \"$1\" in\n /*)\n echo \"Error: Absolute paths not allowed\" >&2\n exit 1\n ;;\nesac\n\nrm -rf \"$1\"\n```\n\n---\n\n## 25. Using -e for File Existence\n\nNot a mistake, but be specific:\n\n```bash\n[ -e \"$file\" ] # Exists (any type)\n[ -f \"$file\" ] # Regular file\n[ -d \"$file\" ] # Directory\n[ -L \"$file\" ] # Symbolic link\n[ -r \"$file\" ] # Readable\n[ -w \"$file\" ] # Writable\n[ -x \"$file\" ] # Executable\n```\n\n---\n\n## Quick Checklist\n\nBefore running a script, verify:\n\n- [ ] Proper shebang (#!/bin/bash or #!/bin/sh)\n- [ ] set -euo pipefail (strict mode)\n- [ ] All variables quoted\n- [ ] Error handling for critical commands\n- [ ] Using $() not backticks\n- [ ] Not using ls for file processing\n- [ ] Functions defined before use\n- [ ] Proper exit codes (0 = success)\n- [ ] Input validation\n- [ ] ShellCheck passes\n\n---\n\n## Resources\n\n- [ShellCheck](https://www.shellcheck.net/) - Catches most of these\n- [Bash Pitfalls](https://mywiki.wooledge.org/BashPitfalls)\n- [POSIX Shell](https://pubs.opengroup.org/onlinepubs/9699919799/)","content_type":"text/markdown; charset=utf-8","language":"markdown","size":9209,"content_sha256":"9dc684ebac8b9d6ac4a2eaf8441230878eee6ce70a14b29f5ed95219228e4829"},{"filename":"docs/grep-reference.md","content":"# GNU grep Reference Guide\n\n## Overview\n\ngrep (Global Regular Expression Print) searches for patterns in text files. It's one of the most commonly used Unix tools.\n\n**Official Manual:** https://www.gnu.org/software/grep/manual/\n**Man Page:** `man grep`\n\n## Basic Syntax\n\n```bash\ngrep [OPTIONS] PATTERN [FILE...]\ngrep [OPTIONS] -e PATTERN ... [FILE...]\ngrep [OPTIONS] -f PATTERN_FILE ... [FILE...]\n```\n\n## Common Options\n\n### Basic Options\n```bash\n-i, --ignore-case # Case-insensitive search\n-v, --invert-match # Invert match (select non-matching lines)\n-w, --word-regexp # Match whole words only\n-x, --line-regexp # Match whole lines only\n-c, --count # Count matching lines\n-n, --line-number # Show line numbers\n-H, --with-filename # Print filename with matches\n-h, --no-filename # Suppress filename output\n-l, --files-with-matches # Print only filenames with matches\n-L, --files-without-match # Print only filenames without matches\n```\n\n### Context Options\n```bash\n-A NUM, --after-context=NUM # Print NUM lines after match\n-B NUM, --before-context=NUM # Print NUM lines before match\n-C NUM, --context=NUM # Print NUM lines before and after\n```\n\n### Regular Expression Options\n```bash\n-E, --extended-regexp # Use Extended Regular Expressions (ERE)\n-F, --fixed-strings # Treat PATTERN as fixed strings, not regex\n-G, --basic-regexp # Use Basic Regular Expressions (BRE) - default\n-P, --perl-regexp # Use Perl-compatible regex (PCRE)\n```\n\n### Output Options\n```bash\n-o, --only-matching # Print only matched parts\n-q, --quiet, --silent # Suppress output, just return exit code\n--color[=WHEN] # Colorize output (auto, always, never)\n-s, --no-messages # Suppress error messages\n```\n\n### File Selection\n```bash\n-r, --recursive # Recursive search\n-R, --dereference-recursive # Recursive, following symlinks\n--include=PATTERN # Search only files matching PATTERN\n--exclude=PATTERN # Skip files matching PATTERN\n--exclude-dir=PATTERN # Skip directories matching PATTERN\n```\n\n## Common Usage Patterns\n\n### Basic Searches\n```bash\n# Simple string search\ngrep \"error\" logfile.txt\n\n# Case-insensitive search\ngrep -i \"error\" logfile.txt\n\n# Search for whole word\ngrep -w \"error\" logfile.txt\n\n# Count matches\ngrep -c \"error\" logfile.txt\n\n# Show line numbers\ngrep -n \"error\" logfile.txt\n```\n\n### Multiple Files\n```bash\n# Search in multiple files\ngrep \"pattern\" file1.txt file2.txt\n\n# Search in all txt files\ngrep \"pattern\" *.txt\n\n# Recursive search\ngrep -r \"pattern\" /path/to/dir\n\n# Recursive with file pattern\ngrep -r --include=\"*.log\" \"error\" /var/log\n```\n\n### Context Display\n```bash\n# Show 3 lines after match\ngrep -A 3 \"error\" logfile.txt\n\n# Show 3 lines before match\ngrep -B 3 \"error\" logfile.txt\n\n# Show 3 lines before and after\ngrep -C 3 \"error\" logfile.txt\n```\n\n### Invert Match\n```bash\n# Show lines that DON'T contain pattern\ngrep -v \"debug\" logfile.txt\n\n# Exclude multiple patterns\ngrep -v \"debug\\|info\" logfile.txt\n```\n\n### Multiple Patterns\n```bash\n# Match any pattern (OR)\ngrep -e \"error\" -e \"warning\" file.txt\ngrep \"error\\|warning\" file.txt\n\n# Match all patterns (AND) - requires pipeline\ngrep \"error\" file.txt | grep \"critical\"\n```\n\n### File Selection\n```bash\n# List files containing match\ngrep -l \"pattern\" *.txt\n\n# List files NOT containing match\ngrep -L \"pattern\" *.txt\n\n# Recursive with excludes\ngrep -r --exclude-dir=\".git\" \"pattern\" .\ngrep -r --exclude=\"*.min.js\" \"pattern\" .\n```\n\n## Regular Expressions in grep\n\n### Basic Regular Expressions (BRE) - Default\n\n```bash\n# BRE Metacharacters (no escaping needed)\n. # Any single character\n^ # Start of line\n$ # End of line\n[...] # Character class\n[^...] # Negated character class\n* # Zero or more of previous\n\n# BRE Metacharacters (MUST be escaped)\n\\+ # One or more (requires \\)\n\\? # Zero or one (requires \\)\n\\{m,n\\} # Between m and n occurrences (requires \\)\n\\(...\\) # Group (requires \\)\n\\| # Alternation (requires \\)\n```\n\n### BRE Examples\n```bash\n# Match lines starting with \"Error\"\ngrep \"^Error\" file.txt\n\n# Match lines ending with \"failed\"\ngrep \"failed$\" file.txt\n\n# Match any character\ngrep \"a.c\" file.txt # Matches abc, aXc, a5c\n\n# Character class\ngrep \"[0-9]\" file.txt # Match any digit\ngrep \"[A-Za-z]\" file.txt # Match any letter\n\n# One or more (escaped)\ngrep \"a\\+b\" file.txt # Matches ab, aab, aaab\n\n# Groups and alternation (escaped)\ngrep \"\\(error\\|warning\\)\" file.txt\n```\n\n### Extended Regular Expressions (ERE) - grep -E\n\n```bash\n# ERE - No escaping needed for +, ?, |, (), {}\n+ # One or more\n? # Zero or one\n{m,n} # Between m and n occurrences\n(...) # Group\n| # Alternation\n```\n\n### ERE Examples\n```bash\n# One or more (no escape)\ngrep -E \"a+b\" file.txt\n\n# Zero or one\ngrep -E \"colou?r\" file.txt # Matches color or colour\n\n# Alternation\ngrep -E \"(error|warning)\" file.txt\n\n# Quantifiers\ngrep -E \"[0-9]{3}-[0-9]{4}\" file.txt # Phone: 123-4567\ngrep -E \"[0-9]{1,3}\" file.txt # 1 to 3 digits\n\n# Groups\ngrep -E \"(http|https)://[^ ]+\" file.txt # URLs\n```\n\n## Character Classes\n\n### POSIX Character Classes\n```bash\n[:alnum:] # Alphanumeric [A-Za-z0-9]\n[:alpha:] # Alphabetic [A-Za-z]\n[:digit:] # Digits [0-9]\n[:lower:] # Lowercase [a-z]\n[:upper:] # Uppercase [A-Z]\n[:space:] # Whitespace [ \\t\\n\\r\\f\\v]\n[:blank:] # Space and tab [ \\t]\n[:punct:] # Punctuation\n[:xdigit:] # Hex digits [0-9A-Fa-f]\n[:word:] # Word characters [A-Za-z0-9_]\n```\n\n### Usage\n```bash\n# Match any digit\ngrep \"[[:digit:]]\" file.txt\n\n# Match any whitespace\ngrep \"[[:space:]]\" file.txt\n\n# Match uppercase letters\ngrep \"[[:upper:]]\" file.txt\n```\n\n## Practical Examples for Shell Scripts\n\n### Log File Analysis\n```bash\n# Find errors in last hour\ngrep \"$(date -d '1 hour ago' '+%Y-%m-%d %H')\" /var/log/app.log | grep -i error\n\n# Count error types\ngrep -i \"error\" logfile.log | cut -d: -f2 | sort | uniq -c | sort -rn\n\n# Extract IP addresses\ngrep -oE '\\b([0-9]{1,3}\\.){3}[0-9]{1,3}\\b' access.log\n```\n\n### Configuration Validation\n```bash\n# Find uncommented lines in config\ngrep -v \"^#\" config.file | grep -v \"^$\"\n\n# Check for specific settings\nif grep -q \"debug = true\" config.ini; then\n echo \"Debug mode is enabled\"\nfi\n```\n\n### Code Search\n```bash\n# Find function definitions\ngrep -n \"^function \" script.sh\n\n# Find TODO comments\ngrep -rn \"TODO\" --include=\"*.sh\" .\n\n# Find unquoted variables (simple check)\ngrep -n '\\$[A-Za-z_][A-Za-z0-9_]*\\s' script.sh\n```\n\n## Performance Tips\n\n### 1. Use -F for Fixed Strings\n```bash\n# Faster when not using regex\ngrep -F \"literal string\" large_file.txt\n```\n\n### 2. Use -q When Only Checking Existence\n```bash\n# Don't need output, just exit code\nif grep -q \"pattern\" file.txt; then\n echo \"Found\"\nfi\n```\n\n### 3. Limit Search Depth\n```bash\n# Don't recurse too deep\ngrep -r --max-depth=2 \"pattern\" /path\n```\n\n### 4. Exclude Unnecessary Directories\n```bash\ngrep -r --exclude-dir={.git,.svn,node_modules} \"pattern\" .\n```\n\n## Exit Codes\n\n- **0**: Match found\n- **1**: No match found\n- **2**: Error occurred\n\n## Common Pitfalls in Shell Scripts\n\n### 1. Not Quoting Patterns with Spaces\n```bash\n# Wrong\ngrep $pattern file.txt\n\n# Right\ngrep \"$pattern\" file.txt\n```\n\n### 2. Using grep in Tests Without -q\n```bash\n# Inefficient\nif [ \"$(grep pattern file)\" ]; then\n\n# Better\nif grep -q pattern file; then\n```\n\n### 3. Useless Use of cat\n```bash\n# Wrong (UUOC)\ncat file | grep pattern\n\n# Right\ngrep pattern file\n# or\n\u003c file grep pattern\n```\n\n### 4. Not Handling No Match Case\n```bash\n# grep returns 1 if no match, can cause set -e to exit\ngrep \"pattern\" file || true\n\n# Or check explicitly\nif grep -q \"pattern\" file; then\n echo \"Found\"\nelse\n echo \"Not found\"\nfi\n```\n\n### 5. Forgetting to Escape Regex Metacharacters\n```bash\n# Wrong - . matches any character\ngrep \"192.168.1.1\" file\n\n# Right - escape the dots\ngrep \"192\\.168\\.1\\.1\" file\n\n# Or use -F for literal match\ngrep -F \"192.168.1.1\" file\n```\n\n## Useful Combinations\n\n```bash\n# Case-insensitive recursive search with line numbers\ngrep -rni \"pattern\" /path\n\n# Count total matches across files\ngrep -r \"pattern\" . | wc -l\n\n# Find and highlight matches\ngrep --color=always \"pattern\" file.txt | less -R\n\n# Search compressed files\nzgrep \"pattern\" file.gz\n\n# Search with extended regex and only show matches\ngrep -Eo \"pattern\" file.txt\n```\n\n## Resources\n\n- [GNU grep Manual](https://www.gnu.org/software/grep/manual/)\n- [grep(1) Linux Man Page](https://man7.org/linux/man-pages/man1/grep.1.html)\n- [Regular Expressions in grep](https://www.gnu.org/software/grep/manual/html_node/Regular-Expressions.html)\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":8691,"content_sha256":"5c321a5678f248b2d585b9275d90df71b41a96b2bff5072f9a6f51996fb73782"},{"filename":"docs/regex-reference.md","content":"# Regular Expressions Reference Guide\n\n## Overview\n\nRegular expressions (regex) are patterns used to match character combinations in strings. POSIX defines two flavors: Basic (BRE) and Extended (ERE).\n\n**POSIX Specification:** https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap09.html\n\n## BRE vs ERE\n\n| Feature | BRE | ERE |\n|---------|-----|-----|\n| One or more | `\\+` | `+` |\n| Zero or one | `\\?` | `?` |\n| Alternation | `\\|` | `|` |\n| Grouping | `\\(...\\)` | `(...)` |\n| Quantifiers | `\\{m,n\\}` | `{m,n}` |\n\n### Tool Usage\n\n```bash\n# BRE (Basic)\ngrep 'pattern' file # BRE by default\nsed 's/pattern/repl/' file # BRE by default\nawk '/pattern/' file # ERE by default\n\n# ERE (Extended)\ngrep -E 'pattern' file # ERE\negrep 'pattern' file # ERE (deprecated, use grep -E)\nsed -E 's/pattern/repl/' file # ERE\n```\n\n## Basic Metacharacters (Both BRE and ERE)\n\n### Single Character Matchers\n```regex\n. # Any single character except newline\n[abc] # Any character in set (a, b, or c)\n[^abc] # Any character NOT in set\n[a-z] # Any character in range\n[0-9] # Any digit\n```\n\n### Anchors\n```regex\n^ # Start of line\n$ # End of line\n\\\u003c # Start of word (GNU extension)\n\\> # End of word (GNU extension)\n\\b # Word boundary (some tools)\n\\B # Not a word boundary (some tools)\n```\n\n### Quantifiers (Zero or More)\n```regex\n* # Zero or more of previous (both BRE and ERE)\n```\n\n## Extended Metacharacters\n\n### ERE Quantifiers\n```regex\n+ # One or more (ERE: +) (BRE: \\+)\n? # Zero or one (ERE: ?) (BRE: \\?)\n{n} # Exactly n (ERE: {n}) (BRE: \\{n\\})\n{n,} # n or more (ERE: {n,}) (BRE: \\{n,\\})\n{n,m} # Between n and m (ERE: {n,m}) (BRE: \\{n,m\\})\n```\n\n### Grouping and Alternation\n```regex\n# ERE\n(pattern) # Group\npattern1|pattern2 # Alternation (OR)\n\n# BRE (requires backslashes)\n\\(pattern\\) # Group\npattern1\\|pattern2 # Alternation (OR)\n```\n\n## POSIX Character Classes\n\nMust be used inside bracket expressions `[[:class:]]`:\n\n```regex\n[:alnum:] # Alphanumeric [A-Za-z0-9]\n[:alpha:] # Alphabetic [A-Za-z]\n[:digit:] # Digits [0-9]\n[:lower:] # Lowercase [a-z]\n[:upper:] # Uppercase [A-Z]\n[:space:] # Whitespace [ \\t\\n\\r\\f\\v]\n[:blank:] # Space and tab [ \\t]\n[:punct:] # Punctuation\n[:xdigit:] # Hexadecimal [0-9A-Fa-f]\n[:word:] # Word characters [A-Za-z0-9_] (GNU extension)\n[:graph:] # Visible characters (not space)\n[:print:] # Printable characters (including space)\n[:cntrl:] # Control characters\n```\n\n### Usage\n```bash\n# Match any digit\ngrep '[[:digit:]]' file\n\n# Match any whitespace\ngrep '[[:space:]]' file\n\n# Match alphanumeric\ngrep '[[:alnum:]]' file\n\n# Negation\ngrep '[^[:digit:]]' file # Not a digit\n```\n\n## Common Patterns\n\n### Numbers\n```bash\n# BRE\n[0-9] # Single digit\n[0-9]\\+ # One or more digits\n[0-9]\\{3\\} # Exactly 3 digits\n[0-9]\\{3,5\\} # 3 to 5 digits\n\n# ERE\n[0-9] # Single digit\n[0-9]+ # One or more digits\n[0-9]{3} # Exactly 3 digits\n[0-9]{3,5} # 3 to 5 digits\n```\n\n### IP Addresses\n```bash\n# Simple (BRE)\ngrep '[0-9]\\{1,3\\}\\.[0-9]\\{1,3\\}\\.[0-9]\\{1,3\\}\\.[0-9]\\{1,3\\}' file\n\n# Simple (ERE)\ngrep -E '[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}' file\n\n# More strict (ERE)\ngrep -E '\\b([0-9]{1,3}\\.){3}[0-9]{1,3}\\b' file\n```\n\n### Email Addresses\n```bash\n# Simple (ERE)\ngrep -E '[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}' file\n\n# BRE\ngrep '[a-zA-Z0-9._%+-]\\+@[a-zA-Z0-9.-]\\+\\.[a-zA-Z]\\{2,\\}' file\n```\n\n### URLs\n```bash\n# Simple HTTP/HTTPS (ERE)\ngrep -E 'https?://[a-zA-Z0-9./?=_-]+' file\n\n# BRE\ngrep 'https\\?://[a-zA-Z0-9./?=_-]\\+' file\n```\n\n### Phone Numbers\n```bash\n# Format: 123-456-7890 (ERE)\ngrep -E '[0-9]{3}-[0-9]{3}-[0-9]{4}' file\n\n# Format: (123) 456-7890 (ERE)\ngrep -E '\\([0-9]{3}\\) [0-9]{3}-[0-9]{4}' file\n\n# BRE\ngrep '\\([0-9]\\{3\\}\\) [0-9]\\{3\\}-[0-9]\\{4\\}' file\n```\n\n### Dates\n```bash\n# YYYY-MM-DD (ERE)\ngrep -E '[0-9]{4}-[0-9]{2}-[0-9]{2}' file\n\n# MM/DD/YYYY (ERE)\ngrep -E '[0-9]{2}/[0-9]{2}/[0-9]{4}' file\n\n# BRE\ngrep '[0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\}' file\n```\n\n## Shell Script Patterns\n\n### Variable Names\n```bash\n# Valid bash variable name (ERE)\ngrep -E '^[a-zA-Z_][a-zA-Z0-9_]*=' file\n\n# Find variable usage\ngrep -E '\\$[a-zA-Z_][a-zA-Z0-9_]*' file\ngrep -E '\\$\\{[a-zA-Z_][a-zA-Z0-9_]*\\}' file\n```\n\n### Function Definitions\n```bash\n# POSIX function (ERE)\ngrep -E '^[a-zA-Z_][a-zA-Z0-9_]*\\s*\\(\\)' file\n\n# Bash function keyword (ERE)\ngrep -E '^function [a-zA-Z_][a-zA-Z0-9_]*' file\n```\n\n### Comments\n```bash\n# Shell comments\ngrep '^[[:space:]]*#' file\n\n# Uncommented lines\ngrep -v '^[[:space:]]*#' file\n```\n\n## Escaping Special Characters\n\nCharacters that need escaping (context-dependent):\n\n```regex\n. * [ ] ^ $ \\ ( ) { } + ? |\n```\n\n### Literal Matching\n```bash\n# Match literal dot (BRE and ERE)\ngrep '\\.' file\n\n# Match literal asterisk\ngrep '\\*' file\n\n# Match literal dollar sign\ngrep '\\

Bash Script Validator Overview This skill validates Bash and POSIX shell scripts with layered checks: 1. Syntax validation ( or ) 2. ShellCheck static analysis (system binary or wrapper fallback) 3. Custom security, portability, and optimization checks Use the default flow below, then branch to fallbacks only when the environment is constrained. Trigger Guidance Use this skill when the request includes script quality, linting, syntax checking, or shell portability work. Trigger Phrases - "Validate this bash script" - "Lint this file" - "Find security issues in this shell script" - "Why does t…

file\n\n# Match literal brackets\ngrep '\\[' file\ngrep '\\]' file\n```\n\n## Backreferences\n\n### Capture and Reuse\n```bash\n# BRE - capture with \\(...\\), reference with \\1, \\2, etc.\nsed 's/\\([0-9]\\+\\)-\\([0-9]\\+\\)/\\2-\\1/' file # Swap numbers\n\n# ERE - capture with (...), reference with \\1, \\2, etc.\nsed -E 's/([0-9]+)-([0-9]+)/\\2-\\1/' file\n\n# Find duplicate words\ngrep -E '\\b([a-z]+) \\1\\b' file\n```\n\n### Examples\n```bash\n# Extract and rearrange (BRE)\nsed 's/\\([A-Z][a-z]*\\), \\([A-Z][a-z]*\\)/\\2 \\1/' file\n\n# ERE\nsed -E 's/([A-Z][a-z]*), ([A-Z][a-z]*)/\\2 \\1/' file\n\n# Find repeated lines\ngrep -E '^(.*)$\\n\\1

Bash Script Validator Overview This skill validates Bash and POSIX shell scripts with layered checks: 1. Syntax validation ( or ) 2. ShellCheck static analysis (system binary or wrapper fallback) 3. Custom security, portability, and optimization checks Use the default flow below, then branch to fallbacks only when the environment is constrained. Trigger Guidance Use this skill when the request includes script quality, linting, syntax checking, or shell portability work. Trigger Phrases - "Validate this bash script" - "Lint this file" - "Find security issues in this shell script" - "Why does t…

file\n```\n\n## Greedy vs Non-Greedy\n\nPOSIX regex is always greedy (matches longest possible string):\n\n```bash\n# Always greedy in POSIX\necho \"foo bar baz\" | grep -o 'f.*b' # Matches \"foo bar b\"\n\n# For non-greedy, you need to be creative\necho \"foo bar baz\" | grep -o 'f[^b]*b' # Matches \"foo b\"\n```\n\n## Lookahead and Lookbehind\n\n**NOT supported in POSIX BRE/ERE**. Only available in PCRE (Perl-Compatible Regular Expressions):\n\n```bash\n# PCRE only (not POSIX)\ngrep -P '(?\u003c=foo)bar' file # Lookbehind\ngrep -P 'foo(?=bar)' file # Lookahead\n```\n\n## Common Mistakes\n\n### 1. Forgetting to Escape in BRE\n```bash\n# Wrong (BRE)\ngrep '(foo|bar)' file\n\n# Right (BRE)\ngrep '\\(foo\\|bar\\)' file\n\n# Or use ERE\ngrep -E '(foo|bar)' file\n```\n\n### 2. Using + in BRE Without Escape\n```bash\n# Wrong (BRE)\ngrep '[0-9]+' file\n\n# Right (BRE)\ngrep '[0-9]\\+' file\n\n# Or use ERE\ngrep -E '[0-9]+' file\n```\n\n### 3. Not Escaping Dots for Literal Match\n```bash\n# Wrong - matches any character\ngrep '192.168.1.1' file\n\n# Right - matches literal dots\ngrep '192\\.168\\.1\\.1' file\n```\n\n### 4. Greedy Matching Issues\n```bash\n# Matches too much\necho '\u003ctag>content\u003c/tag>' | sed 's/\u003c.*>//' # Empty!\n\n# Better\necho '\u003ctag>content\u003c/tag>' | sed 's/\u003c[^>]*>//'\n```\n\n### 5. Character Class Mistakes\n```bash\n# Wrong - not a range\ngrep '[a-Z]' file # Undefined behavior\n\n# Right\ngrep '[a-zA-Z]' file\n\n# Or use POSIX class\ngrep '[[:alpha:]]' file\n```\n\n## Testing Regex\n\n### Online Tools\n- regex101.com (supports PCRE, not POSIX)\n- regexr.com\n- regexpal.com\n\n### Command Line Testing\n```bash\n# Test with echo\necho \"test string\" | grep 'pattern'\n\n# Show matches only\necho \"test string\" | grep -o 'pattern'\n\n# Test with multiple lines\nprintf 'line1\\nline2\\nline3\\n' | grep 'pattern'\n\n# Color highlighting\ngrep --color=always 'pattern' file | less -R\n```\n\n## Quick Reference Table\n\n| Pattern | BRE | ERE | Matches |\n|---------|-----|-----|---------|\n| Literal | `abc` | `abc` | abc |\n| Any char | `.` | `.` | any single character |\n| Start | `^` | `^` | start of line |\n| End | ` bash-script-validator — Skillopedia | ` bash-script-validator — Skillopedia | end of line |\n| Zero or more | `*` | `*` | 0+ of previous |\n| One or more | `\\+` | `+` | 1+ of previous |\n| Zero or one | `\\?` | `?` | 0 or 1 of previous |\n| Exactly n | `\\{n\\}` | `{n}` | exactly n |\n| n or more | `\\{n,\\}` | `{n,}` | n or more |\n| n to m | `\\{n,m\\}` | `{n,m}` | between n and m |\n| Group | `\\(...\\)` | `(...)` | capture group |\n| Alternation | `\\|` | `|` | OR |\n| Character class | `[abc]` | `[abc]` | a, b, or c |\n| Negated class | `[^abc]` | `[^abc]` | not a, b, or c |\n| Range | `[a-z]` | `[a-z]` | lowercase letters |\n\n## Resources\n\n- [POSIX Regular Expressions](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap09.html)\n- [Regular-Expressions.info](https://www.regular-expressions.info/posix.html)\n- [GNU grep Manual - Regular Expressions](https://www.gnu.org/software/grep/manual/html_node/Regular-Expressions.html)","content_type":"text/markdown; charset=utf-8","language":"markdown","size":8587,"content_sha256":"dac74a3d837a1eaf881618e89cc758f9c05b2270912ca4eb67bcb72be748c221"},{"filename":"docs/sed-reference.md","content":"# GNU sed Reference Guide\n\n## Overview\n\nsed (Stream EDitor) is a powerful text processing tool that performs basic text transformations on an input stream (file or pipeline).\n\n**Official Manual:** https://www.gnu.org/software/sed/manual/\n**Man Page:** `man sed`\n\n## Basic Syntax\n\n```bash\nsed [OPTIONS] 'command' file\nsed [OPTIONS] -e 'command1' -e 'command2' file\nsed [OPTIONS] -f script.sed file\n```\n\n## Common Options\n\n```bash\n-n, --quiet, --silent # Suppress automatic output\n-e SCRIPT # Add script to commands\n-f FILE # Add contents of FILE as commands\n-i[SUFFIX] # Edit files in-place\n-r, -E # Use extended regular expressions\n--debug # Annotate program execution\n```\n\n## Basic Commands\n\n### Substitution (s)\n```bash\n# Basic substitution\nsed 's/old/new/' file # Replace first occurrence\nsed 's/old/new/g' file # Replace all occurrences\nsed 's/old/new/2' file # Replace second occurrence\nsed 's/old/new/gi' file # Case-insensitive, all\n\n# With different delimiter\nsed 's|/old/path|/new/path|g' file\nsed 's#old#new#g' file\n```\n\n### Deletion (d)\n```bash\n# Delete lines\nsed '5d' file # Delete line 5\nsed '5,10d' file # Delete lines 5-10\nsed '/pattern/d' file # Delete matching lines\nsed '/^$/d' file # Delete empty lines\nsed '/^#/d' file # Delete comment lines\n```\n\n### Print (p)\n```bash\n# Print lines\nsed -n '5p' file # Print only line 5\nsed -n '5,10p' file # Print lines 5-10\nsed -n '/pattern/p' file # Print matching lines\n```\n\n### Append (a), Insert (i), Change (c)\n```bash\n# Append after line\nsed '5a\\New line' file\n\n# Insert before line\nsed '5i\\New line' file\n\n# Change line\nsed '5c\\Replacement line' file\n\n# With pattern\nsed '/pattern/a\\New line after match' file\n```\n\n## Address Ranges\n\n### Line Numbers\n```bash\nsed '5s/old/new/' file # Line 5 only\nsed '5,10s/old/new/' file # Lines 5-10\nsed '5,$s/old/new/' file # Line 5 to end\nsed '1,5d' file # Delete first 5 lines\n```\n\n### Patterns\n```bash\nsed '/start/,/end/d' file # Delete from start to end pattern\nsed '/pattern/s/old/new/' file # Substitute in matching lines\nsed '1,/pattern/d' file # Delete from line 1 to first match\n```\n\n### Special Addresses\n```bash\nsed '$d' file # Delete last line\nsed '1d' file # Delete first line\nsed '$s/old/new/' file # Substitute in last line\n```\n\n## Advanced Substitution\n\n### Backreferences\n```bash\n# Capture and reuse\nsed 's/\\([0-9]\\+\\)/Number: \\1/' file\n\n# Multiple captures\nsed 's/\\([a-z]\\+\\) \\([0-9]\\+\\)/\\2 \\1/' file\n\n# With ERE (-E or -r)\nsed -E 's/([0-9]+)/Number: \\1/' file\nsed -E 's/([a-z]+) ([0-9]+)/\\2 \\1/' file\n```\n\n### Flags\n```bash\ns/old/new/ # Replace first\ns/old/new/g # Replace all (global)\ns/old/new/2 # Replace 2nd occurrence\ns/old/new/i # Case-insensitive\ns/old/new/I # Case-insensitive (same as i)\ns/old/new/p # Print if substitution made\ns/old/new/w file # Write if substitution made\n```\n\n### Special Characters in Replacement\n```bash\n& # Matched string\n\\1, \\2, etc # Backreferences\n\\L, \\U # Convert to lower/upper (GNU sed)\n\\n # Newline (in replacement)\n\\\\ # Literal backslash\n```\n\n## Multiple Commands\n\n### Multiple -e Options\n```bash\nsed -e 's/old/new/g' -e 's/foo/bar/g' file\n```\n\n### Semicolon Separator\n```bash\nsed 's/old/new/g; s/foo/bar/g' file\n```\n\n### Multi-line Script\n```bash\nsed '\ns/old/new/g\ns/foo/bar/g\n/pattern/d\n' file\n```\n\n## In-place Editing\n\n```bash\n# Edit file in-place\nsed -i 's/old/new/g' file\n\n# Create backup\nsed -i.bak 's/old/new/g' file\n\n# Multiple files\nsed -i 's/old/new/g' *.txt\n```\n\n## Pattern Matching\n\n### BRE (Basic Regular Expressions) - Default\n```bash\nsed 's/^/#/' file # Add # at beginning\nsed 's/$/;/' file # Add ; at end\nsed 's/[0-9]\\+/X/g' file # Replace numbers (BRE)\nsed 's/\\\u003cword\\>/WORD/g' file # Word boundaries (BRE)\n```\n\n### ERE (Extended Regular Expressions)\n```bash\nsed -E 's/[0-9]+/X/g' file # Replace numbers (ERE)\nsed -E 's/(foo|bar)/baz/g' file # Alternation\nsed -E 's/\\s+/ /g' file # Multiple spaces to one\n```\n\n## Practical Examples for Shell Scripts\n\n### Configuration File Editing\n```bash\n# Change a config value\nsed -i 's/^Port .*/Port 2222/' /etc/ssh/sshd_config\n\n# Uncomment a line\nsed -i 's/^#\\(.*option.*\\)/\\1/' config.file\n\n# Comment out a line\nsed -i 's/^\\(.*dangerous.*\\)/#\\1/' config.file\n\n# Add line after pattern\nsed -i '/\\[section\\]/a new_setting = value' config.ini\n```\n\n### Text Processing\n```bash\n# Remove trailing whitespace\nsed 's/[[:space:]]*$//' file\n\n# Remove leading whitespace\nsed 's/^[[:space:]]*//' file\n\n# Remove empty lines\nsed '/^$/d' file\n\n# Remove comments and empty lines\nsed '/^#/d; /^$/d' file\n\n# Double-space file\nsed 'G' file\n\n# Remove duplicate lines (consecutive)\nsed '$!N; /^\\(.*\\)\\n\\1$/!P; D' file\n```\n\n### Path Manipulation\n```bash\n# Change paths\nsed 's|/old/path|/new/path|g' file\n\n# Extract filename from path\necho \"/path/to/file.txt\" | sed 's|.*/||'\n\n# Extract directory from path\necho \"/path/to/file.txt\" | sed 's|/[^/]*$||'\n```\n\n### Log File Processing\n```bash\n# Extract IP addresses\nsed -n 's/.*\\([0-9]\\{1,3\\}\\.[0-9]\\{1,3\\}\\.[0-9]\\{1,3\\}\\.[0-9]\\{1,3\\}\\).*/\\1/p' log\n\n# Filter by date\nsed -n '/2025-01-01/,/2025-01-31/p' log\n\n# Remove timestamp\nsed 's/^[0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\} [0-9]\\{2\\}:[0-9]\\{2\\}:[0-9]\\{2\\} //' log\n```\n\n### Code Refactoring\n```bash\n# Rename function\nsed -i 's/\\boldFunctionName\\b/newFunctionName/g' *.sh\n\n# Change variable\nsed -i 's/\\$old_var/\\$new_var/g' script.sh\n\n# Update shebang\nsed -i '1s|^#!/bin/sh|#!/bin/bash|' *.sh\n```\n\n## Advanced Features\n\n### Hold Space\n```bash\n# Hold space commands\nh # Copy pattern space to hold space\nH # Append pattern space to hold space\ng # Copy hold space to pattern space\nG # Append hold space to pattern space\nx # Exchange pattern and hold spaces\n```\n\n### Hold Space Examples\n```bash\n# Reverse file\nsed '1!G;h;$!d' file\n\n# Print last line\nsed -n '$p' file\nsed -n 'h;$p' file\n\n# Append next line to current\nsed 'N;s/\\n/ /' file\n```\n\n### Branching\n```bash\n# Label and branch\n:label # Define label\nb label # Branch to label\nt label # Branch if substitution succeeded\nT label # Branch if substitution failed\n```\n\n### Branching Examples\n```bash\n# Remove C-style comments\nsed -n '\n/\\/\\*/ {\n :loop\n /\\*\\// {\n s|/\\*.*\\*/||g\n p\n b\n }\n N\n b loop\n}\np\n' file\n```\n\n## Common Patterns\n\n### Join Lines\n```bash\n# Join all lines\nsed ':a;N;$!ba;s/\\n/ /g' file\n\n# Join lines ending with backslash\nsed -e :a -e '/\\\\$/N; s/\\\\\\n//; ta' file\n```\n\n### Number Lines\n```bash\nsed = file | sed 'N;s/\\n/\\t/'\n```\n\n### Reverse Lines\n```bash\nsed '1!G;h;$!d' file\n```\n\n### Print Specific Lines\n```bash\n# Print line 5\nsed -n '5p' file\n\n# Print first 10 lines\nsed -n '1,10p' file\n\n# Print last 10 lines\nsed -n -e :a -e '1,10!{P;N;D;};N;ba' file\n```\n\n## Common Pitfalls in Shell Scripts\n\n### 1. Special Characters in Pattern\n```bash\n# Wrong - . matches any character\nsed 's/192.168.1.1/new/' file\n\n# Right - escape dots\nsed 's/192\\.168\\.1\\.1/new/' file\n\n# Or use different delimiter\nsed 's|192.168.1.1|new|' file\n```\n\n### 2. Not Escaping Backreferences in BRE\n```bash\n# Wrong (BRE)\nsed 's/([0-9]+)/\\1/' file\n\n# Right (BRE)\nsed 's/\\([0-9]\\+\\)/\\1/' file\n\n# Right (ERE)\nsed -E 's/([0-9]+)/\\1/' file\n```\n\n### 3. In-place Editing Without Backup\n```bash\n# Dangerous\nsed -i 's/old/new/' important_file\n\n# Safer\nsed -i.backup 's/old/new/' important_file\n```\n\n### 4. Using sed for Line Counting\n```bash\n# Inefficient\nsed -n '$=' file\n\n# Better\nwc -l \u003c file\n```\n\n### 5. Not Quoting Variables Properly\n```bash\n# Wrong\nsed \"s/$old/$new/g\" file # Dangerous if vars contain /\n\n# Better\nold_escaped=$(printf '%s\\n' \"$old\" | sed 's:[\\\\/&]:\\\\&:g')\nnew_escaped=$(printf '%s\\n' \"$new\" | sed 's:[\\\\/&]:\\\\&:g')\nsed \"s/$old_escaped/$new_escaped/g\" file\n\n# Or use different delimiter\nsed \"s|$old|$new|g\" file\n```\n\n## Performance Tips\n\n### 1. Use Appropriate Tools\n```bash\n# For simple replacements, consider using other tools\n# sed is great for complex patterns, but for simple tasks:\n\n# Instead of\nsed 's/old/new/g' file\n\n# Consider\ntr 'old' 'new' \u003c file # For single-character replacement\n```\n\n### 2. Minimize Pattern Matching\n```bash\n# Less efficient\nsed '/pattern/s/old/new/g' large_file\n\n# More efficient if pattern is rare\ngrep 'pattern' large_file | sed 's/old/new/g'\n```\n\n### 3. Combine Commands\n```bash\n# Less efficient\nsed 's/old/new/g' file | sed 's/foo/bar/g'\n\n# More efficient\nsed 's/old/new/g; s/foo/bar/g' file\n```\n\n## Testing sed Commands\n\n```bash\n# Test before in-place edit\nsed 's/old/new/g' file | head\n\n# Show only changes\nsed -n 's/old/new/gp' file\n\n# Count changes\nsed -n 's/old/new/gp' file | wc -l\n```\n\n## Resources\n\n- [GNU sed Manual](https://www.gnu.org/software/sed/manual/)\n- [sed(1) Man Page](https://man7.org/linux/man-pages/man1/sed.1.html)\n- [sed One-Liners](http://sed.sourceforge.net/sed1line.txt)\n- [Grymoire sed Tutorial](https://www.grymoire.com/Unix/Sed.html)","content_type":"text/markdown; charset=utf-8","language":"markdown","size":9414,"content_sha256":"ea0a93ce4398ef095a8dcb6839b7721047ef684e2102126ed562835ca299e9d3"},{"filename":"docs/shell-reference.md","content":"# POSIX Shell (sh) Reference Guide\n\n## Overview\n\nPOSIX sh is the portable shell specification defined by POSIX standards. Scripts written for POSIX sh should work across different Unix-like systems (bash, dash, ksh, etc.).\n\n**Official Specification:** https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html\n\n## Why POSIX Shell Matters\n\n- **Portability**: Works across different Unix systems and shells\n- **Minimal dependencies**: Available in minimal environments (containers, embedded systems)\n- **Faster**: Shells like dash are faster than bash for simple scripts\n- **Compatibility**: /bin/sh may not be bash (Ubuntu/Debian use dash)\n\n## Key Differences: sh vs bash\n\n### Features NOT Available in POSIX sh\n\n1. **Arrays**\n ```bash\n # Bash only - NOT POSIX\n array=(one two three)\n echo \"${array[0]}\"\n ```\n\n2. **[[ ]] Test Construct**\n ```bash\n # Bash only - NOT POSIX\n if [[ \"$var\" == \"value\" ]]; then\n\n # POSIX sh - use [ ]\n if [ \"$var\" = \"value\" ]; then\n ```\n\n3. **== Operator**\n ```bash\n # Bash style - NOT POSIX\n [ \"$a\" == \"$b\" ]\n\n # POSIX sh - use single =\n [ \"$a\" = \"$b\" ]\n ```\n\n4. **Process Substitution**\n ```bash\n # Bash only - NOT POSIX\n diff \u003c(ls dir1) \u003c(ls dir2)\n ```\n\n5. **Brace Expansion**\n ```bash\n # Bash only - NOT POSIX\n echo {1..10}\n ```\n\n6. **function Keyword**\n ```bash\n # Bash style - NOT in original POSIX\n function myfunc {\n echo \"hello\"\n }\n\n # POSIX sh style\n myfunc() {\n echo \"hello\"\n }\n ```\n\n7. **local Keyword**\n ```bash\n # Common but not in POSIX standard\n local var=\"value\"\n\n # POSIX alternative: use function scope carefully\n # or use naming conventions\n _func_var=\"value\"\n ```\n\n8. **source Command**\n ```bash\n # Bash style - NOT POSIX\n source script.sh\n\n # POSIX sh\n . script.sh\n ```\n\n## POSIX Shell Syntax\n\n### Variables\n\n```sh\n# Assignment\nvar=\"value\"\nreadonly CONST=\"constant\"\n\n# Reading variables\necho \"$var\"\necho \"${var}\"\n\n# Command substitution (POSIX)\nresult=$(command)\n\n# Old-style command substitution (works but deprecated)\nresult=`command`\n\n# Arithmetic (POSIX way)\nresult=$((5 + 3))\n```\n\n### Quoting\n\n```sh\n# Double quotes: Preserve literal value except $, `, and \\\necho \"Value: $var\"\n\n# Single quotes: Preserve everything literally\necho 'Value: $var'\n\n# Always quote variables\ncp \"$file\" \"$destination\"\n```\n\n### Control Structures\n\n```sh\n# If statement\nif [ condition ]; then\n # commands\nelif [ condition ]; then\n # commands\nelse\n # commands\nfi\n\n# Case statement\ncase \"$var\" in\n pattern1)\n # commands\n ;;\n pattern2|pattern3)\n # commands\n ;;\n *)\n # default\n ;;\nesac\n\n# For loop\nfor item in list; do\n echo \"$item\"\ndone\n\n# While loop\nwhile [ condition ]; do\n # commands\ndone\n\n# Until loop\nuntil [ condition ]; do\n # commands\ndone\n```\n\n### Test Constructs\n\nPOSIX sh uses `[ ]` (also known as `test` command):\n\n```sh\n# String comparisons\n[ \"$a\" = \"$b\" ] # Equal\n[ \"$a\" != \"$b\" ] # Not equal\n[ -z \"$a\" ] # String is empty\n[ -n \"$a\" ] # String is not empty\n\n# Numeric comparisons\n[ \"$a\" -eq \"$b\" ] # Equal\n[ \"$a\" -ne \"$b\" ] # Not equal\n[ \"$a\" -lt \"$b\" ] # Less than\n[ \"$a\" -le \"$b\" ] # Less than or equal\n[ \"$a\" -gt \"$b\" ] # Greater than\n[ \"$a\" -ge \"$b\" ] # Greater than or equal\n\n# File tests\n[ -e \"$file\" ] # File exists\n[ -f \"$file\" ] # Regular file exists\n[ -d \"$file\" ] # Directory exists\n[ -r \"$file\" ] # File is readable\n[ -w \"$file\" ] # File is writable\n[ -x \"$file\" ] # File is executable\n[ -s \"$file\" ] # File is not empty\n\n# Logical operators\n[ condition1 ] && [ condition2 ] # AND\n[ condition1 ] || [ condition2 ] # OR\n[ ! condition ] # NOT\n[ condition1 -a condition2 ] # AND (inside test)\n[ condition1 -o condition2 ] # OR (inside test)\n```\n\n### Functions\n\n```sh\n# POSIX function definition\nfunction_name() {\n # No 'local' in strict POSIX\n # Use careful scoping or naming conventions\n echo \"$1\" # First argument\n return 0 # Exit status\n}\n\n# Call function\nfunction_name arg1 arg2\n```\n\n### Input/Output Redirection\n\n```sh\n# Redirect stdout\ncommand > file\n\n# Redirect stderr\ncommand 2> errors.txt\n\n# Redirect both\ncommand > output.txt 2>&1\n\n# Append\ncommand >> file\n\n# Here document\ncat \u003c\u003cEOF\nmultiple\nlines\nof text\nEOF\n\n# Read from stdin\nwhile read -r line; do\n echo \"$line\"\ndone \u003c file.txt\n```\n\n## POSIX Best Practices\n\n### 1. Proper Shebang\n```sh\n#!/bin/sh\n# Use /bin/sh for POSIX scripts, not /bin/bash\n```\n\n### 2. Quote All Variables\n```sh\n# Good\ncp \"$source\" \"$destination\"\n\n# Bad\ncp $source $destination\n```\n\n### 3. Use = Not ==\n```sh\n# POSIX compliant\nif [ \"$var\" = \"value\" ]; then\n\n# NOT POSIX (bash-specific)\nif [ \"$var\" == \"value\" ]; then\n```\n\n### 4. Use $() for Command Substitution\n```sh\n# Preferred (POSIX)\nresult=$(command)\n\n# Old style (works but less readable)\nresult=`command`\n```\n\n### 5. Avoid Bashisms\n\nDon't use:\n- Arrays: `array=(one two)`\n- `[[` test construct\n- Process substitution: `\u003c(command)`\n- Brace expansion: `{1..10}`\n- `function` keyword\n- `source` command (use `.` instead)\n- `==` operator (use `=`)\n- `$RANDOM` variable\n\n### 6. Check Command Existence\n```sh\nif command -v shellcheck >/dev/null 2>&1; then\n echo \"ShellCheck is installed\"\nfi\n```\n\n### 7. Handle Errors\n```sh\n# Set errexit\nset -e\n\n# Or check manually\nif ! command; then\n echo \"Command failed\" >&2\n exit 1\nfi\n```\n\n### 8. Use set -u for Undefined Variables\n```sh\nset -u\n# Now accessing undefined variables causes error\n```\n\n## Common Portability Issues\n\n### 1. echo Command\n```sh\n# Portable way to echo without newline\nprintf '%s' \"text without newline\"\n\n# echo -n is not portable\necho -n \"text\" # Don't use in POSIX sh\n\n# echo with backslashes\nprintf '%s\\n' \"text\\twith\\ttabs\"\necho \"text\\twith\\ttabs\" # Behavior varies\n```\n\n### 2. Array Alternatives\n```sh\n# Instead of arrays, use:\n\n# 1. Positional parameters\nset -- one two three\necho \"$1\" # one\n\n# 2. Delimited strings\nitems=\"one:two:three\"\nIFS=:\nfor item in $items; do\n echo \"$item\"\ndone\n```\n\n### 3. String Manipulation\n```sh\n# POSIX parameter expansion\n${var#pattern} # Remove shortest match from beginning\n${var##pattern} # Remove longest match from beginning\n${var%pattern} # Remove shortest match from end\n${var%%pattern} # Remove longest match from end\n\n# NOT POSIX (bash-specific)\n${var/pattern/replacement}\n${var,,} # lowercase\n${var^^} # uppercase\n```\n\n### 4. Arithmetic\n```sh\n# POSIX way\nresult=$((a + b))\n\n# NOT POSIX (bash-specific)\n((a++))\nlet \"a = a + 1\"\n```\n\n### 5. read Command\n```sh\n# POSIX\nwhile IFS= read -r line; do\n echo \"$line\"\ndone \u003c file\n\n# Bash-specific flags to avoid:\nread -p \"prompt\" # Not in POSIX\nread -a array # Not in POSIX\nread -t timeout # Not in POSIX\n```\n\n## POSIX Parameter Expansion\n\n```sh\n${var} # Value of var\n${var:-default} # Use default if var is unset or null\n${var:=default} # Assign default if var is unset or null\n${var:?error} # Error if var is unset or null\n${var:+alternate} # Use alternate if var is set and not null\n${#var} # Length of var\n${var#pattern} # Remove shortest match from beginning\n${var##pattern} # Remove longest match from beginning\n${var%pattern} # Remove shortest match from end\n${var%%pattern} # Remove longest match from end\n```\n\n## Special Variables (POSIX)\n\n```sh\n$0 # Script name\n$1-$9 # Positional parameters\n${10} # Parameters beyond 9 (braces required)\n$# # Number of positional parameters\n$* # All positional parameters (as single word)\n$@ # All positional parameters (as separate words)\n$ # Process ID of shell\n$! # PID of last background command\n$? # Exit status of last command\n```\n\n## Testing for POSIX Compliance\n\n### Use checkbashisms\n```sh\n# Install checkbashisms (Debian/Ubuntu)\napt-get install devscripts\n\n# Check script\ncheckbashisms script.sh\n```\n\n### Use ShellCheck with sh\n```sh\n# Validate as sh script\nshellcheck -s sh script.sh\n```\n\n### Test with Different Shells\n```sh\n# Test with dash (common /bin/sh)\ndash script.sh\n\n# Test with ash\nash script.sh\n\n# Test with ksh\nksh script.sh\n```\n\n## Common POSIX Utilities\n\nThese utilities are standardized and safe to use in POSIX scripts:\n\n- `cat`, `echo`, `printf`\n- `grep`, `sed`, `awk`\n- `cut`, `sort`, `uniq`, `tr`\n- `head`, `tail`, `wc`\n- `find`, `xargs`\n- `test` (same as `[ ]`)\n- `cd`, `pwd`, `ls`\n- `cp`, `mv`, `rm`, `mkdir`\n- `chmod`, `chown`\n- `read`, `shift`, `set`, `export`\n\n## Resources\n\n- [POSIX Shell Command Language](https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html)\n- [Dash as /bin/sh](https://wiki.ubuntu.com/DashAsBinSh)\n- [Autoconf Portable Shell](https://www.gnu.org/software/autoconf/manual/autoconf.html#Portable-Shell)\n- [Rich's sh (POSIX shell) tricks](http://www.etalabs.net/sh_tricks.html)","content_type":"text/markdown; charset=utf-8","language":"markdown","size":8942,"content_sha256":"e3bbd898a23573b2324086920d0edc30b1af0bcc93fe05fa61e225f1eaa74ded"},{"filename":"docs/shellcheck-reference.md","content":"# ShellCheck Reference Guide\n\n## Overview\n\nShellCheck is a static analysis tool for shell scripts that provides warnings and suggestions for syntax and semantic issues to improve script quality and prevent errors.\n\n**Official Website:** https://www.shellcheck.net/\n**GitHub:** https://github.com/koalaman/shellcheck\n**Wiki:** https://github.com/koalaman/shellcheck/wiki\n\n## Installation\n\n```bash\n# macOS\nbrew install shellcheck\n\n# Ubuntu/Debian\napt-get install shellcheck\n\n# Fedora\ndnf install shellcheck\n\n# From source/binary\n# See: https://github.com/koalaman/shellcheck#installing\n```\n\n## Basic Usage\n\n```bash\n# Check a script\nshellcheck script.sh\n\n# Specify shell dialect\nshellcheck -s bash script.sh\nshellcheck -s sh script.sh\nshellcheck -s ksh script.sh\nshellcheck -s zsh script.sh\n\n# Different output formats\nshellcheck -f gcc script.sh # GCC-style (for editors)\nshellcheck -f checkstyle script.sh # Checkstyle XML\nshellcheck -f json script.sh # JSON\nshellcheck -f tty script.sh # TTY (default, with colors)\n\n# Check multiple files\nshellcheck *.sh\n\n# Exclude specific warnings\nshellcheck -e SC2086,SC2046 script.sh\n\n# Set minimum severity\nshellcheck -S error script.sh # Only errors\nshellcheck -S warning script.sh # Warnings and above\n```\n\n## Severity Levels\n\nShellCheck categorizes issues into four severity levels:\n\n1. **error** - Critical issues that will cause failures\n2. **warning** - Potential bugs or problematic patterns\n3. **info** - Suggestions for improvement\n4. **style** - Stylistic improvements\n\n```bash\n# Show only errors\nshellcheck -S error script.sh\n\n# Show errors and warnings\nshellcheck -S warning script.sh\n\n# Show everything (default)\nshellcheck script.sh\n```\n\n## Common Error Codes\n\n### Critical Errors (SC2xxx series)\n\n#### SC2086: Quote Variables to Prevent Word Splitting\n```bash\n# Problematic\ncp $file $destination\n\n# Fixed\ncp \"$file\" \"$destination\"\n```\n\n#### SC2046: Quote Command Substitutions\n```bash\n# Problematic\nfor file in $(ls *.txt); do\n\n# Fixed\nfor file in *.txt; do\n```\n\n#### SC2006: Use $() Instead of Backticks\n```bash\n# Problematic\nresult=`command`\n\n# Fixed\nresult=$(command)\n```\n\n#### SC2155: Declare and Assign Separately\n```bash\n# Problematic\nlocal result=$(command) # Masks return value\n\n# Fixed\nlocal result\nresult=$(command)\n```\n\n#### SC2164: Use || exit After cd\n```bash\n# Problematic\ncd /some/directory\nrm -rf *\n\n# Fixed\ncd /some/directory || exit\nrm -rf *\n```\n\n#### SC2181: Check Exit Code Directly\n```bash\n# Problematic\ncommand\nif [ $? -eq 0 ]; then\n\n# Fixed\nif command; then\n```\n\n#### SC2068: Quote Array Expansions\n```bash\n# Problematic\ncommand $@\n\n# Fixed\ncommand \"$@\"\n```\n\n#### SC2116: Useless echo with $()\n```bash\n# Problematic\nvar=$(echo $value)\n\n# Fixed\nvar=$value\n```\n\n#### SC2162: read Without -r\n```bash\n# Problematic\nwhile read line; do\n\n# Fixed\nwhile IFS= read -r line; do\n```\n\n#### SC2005: Useless echo Piped to Command\n```bash\n# Problematic\necho \"$var\" | grep pattern\n\n# Fixed\ngrep pattern \u003c\u003c\u003c \"$var\"\n# Or\nprintf '%s\\n' \"$var\" | grep pattern\n```\n\n### Bashisms (SC3xxx series)\n\nThese warn about bash-specific features used in sh scripts:\n\n#### SC3001: Using Bash [[ ]] in sh Script\n```bash\n# In #!/bin/sh script\nif [[ condition ]]; then # Wrong\n\n# Fixed\nif [ condition ]; then\n```\n\n#### SC3037: Using Bash Arrays in sh Script\n```bash\n# In #!/bin/sh script\narray=(one two) # Wrong\n\n# No direct fix - arrays not in POSIX sh\n# Use alternatives like positional parameters\n```\n\n## Disabling Checks\n\n### Disable Specific Line\n```bash\n# shellcheck disable=SC2086\nvariable=$unquoted\n```\n\n### Disable for Entire File\n```bash\n# At top of file\n# shellcheck disable=SC2086,SC2046\n```\n\n### Disable Next Line\n```bash\n# shellcheck disable=SC2086\nvariable=$unquoted\n```\n\n### Disable for Block\n```bash\n# shellcheck disable=SC2086\n{\n var1=$unquoted1\n var2=$unquoted2\n}\n# shellcheck enable=SC2086\n```\n\n## ShellCheck Directives\n\n### Shell Directive\n```bash\n# Specify shell dialect (overrides shebang)\n# shellcheck shell=bash\n# or\n# shellcheck shell=sh\n```\n\n### Source Directive\n```bash\n# Tell ShellCheck where to find sourced files\n# shellcheck source=./lib/common.sh\n. ./lib/common.sh\n```\n\n### External Sources\n```bash\n# For dynamically sourced files\n# shellcheck source=/dev/null\n. \"$config_file\"\n```\n\n## Configuration File\n\nCreate `.shellcheckrc` in project root or `~/.shellcheckrc`:\n\n```bash\n# Disable specific checks globally\ndisable=SC2086,SC2046,SC2068\n\n# Enable optional checks\nenable=all\nenable=avoid-nullary-conditions\n\n# Specify shell\nshell=bash\n```\n\n## Integration with CI/CD\n\n### GitHub Actions\n```yaml\n- name: Run ShellCheck\n uses: ludeeus/action-shellcheck@master\n with:\n severity: warning\n```\n\n### GitLab CI\n```yaml\nshellcheck:\n script:\n - shellcheck **/*.sh\n```\n\n### Pre-commit Hook\n```yaml\n# .pre-commit-config.yaml\n- repo: https://github.com/shellcheck-py/shellcheck-py\n rev: v0.9.0.2\n hooks:\n - id: shellcheck\n```\n\n## Common Patterns and Best Practices\n\n### 1. Always Quote Variables\nShellCheck will flag unquoted variables in most contexts.\n\n### 2. Use -r Flag with read\n```bash\n# Good\nwhile IFS= read -r line; do\n echo \"$line\"\ndone \u003c file\n```\n\n### 3. Check Command Existence\n```bash\nif command -v shellcheck >/dev/null 2>&1; then\n echo \"Found\"\nfi\n```\n\n### 4. Use || exit After cd\n```bash\ncd /directory || exit 1\n```\n\n### 5. Use [[ ]] in Bash, [ ] in sh\nShellCheck knows your shell and will warn appropriately.\n\n### 6. Proper Array Usage\n```bash\n# Good (bash)\nargs=(\"first arg\" \"second arg\")\ncommand \"${args[@]}\"\n```\n\n### 7. Avoid Useless cat\n```bash\n# Instead of\ncat file | grep pattern\n\n# Use\ngrep pattern file\n# or\n\u003c file grep pattern\n```\n\n## Advanced Features\n\n### Optional Checks\nSome checks are not enabled by default:\n\n```bash\n# Enable all optional checks\n# shellcheck enable=all\n\n# Or specific ones\n# shellcheck enable=avoid-nullary-conditions\n# shellcheck enable=quote-safe-variables\n# shellcheck enable=require-variable-braces\n```\n\n### Custom Severity\n```bash\n# Change severity of specific check\n# shellcheck severity=warning SC2086\n```\n\n## Exit Codes\n\n- **0**: No issues found\n- **1**: Some issues found\n- **2**: Syntax errors that prevent parsing\n- **3**: ShellCheck error (bad options, missing files)\n- **4**: ShellCheck not installed\n\n## Editor Integration\n\nShellCheck integrates with most editors:\n\n- **VS Code**: ShellCheck extension\n- **Vim**: via ALE, Syntastic, or vim-shellcheck\n- **Emacs**: flycheck-shellcheck\n- **Sublime Text**: SublimeLinter-shellcheck\n- **Atom**: linter-shellcheck\n\n## Resources\n\n- **Main Website**: https://www.shellcheck.net/\n- **Wiki with Error Codes**: https://github.com/koalaman/shellcheck/wiki\n- **Try Online**: https://www.shellcheck.net/\n- **GitHub Issues**: https://github.com/koalaman/shellcheck/issues\n\n## Quick Reference Table\n\n| Code | Issue | Fix |\n|------|-------|-----|\n| SC2086 | Unquoted variable | Add quotes: `\"$var\"` |\n| SC2046 | Unquoted $() | Quote command substitution |\n| SC2006 | Backticks | Use `$()` instead |\n| SC2155 | Declare and assign together | Separate into two lines |\n| SC2164 | cd without error check | Add `|| exit` |\n| SC2181 | Checking $? | Check command directly |\n| SC2068 | Unquoted $@ | Quote: `\"$@\"` |\n| SC2162 | read without -r | Add `-r` flag |\n| SC3001 | [[ in sh script | Use [ ] instead |\n| SC3037 | Arrays in sh script | Use POSIX alternatives |\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":7366,"content_sha256":"5981221442641f009e91e792fb28a5685d1c3737395179a0391f43ee390286c9"},{"filename":"examples/bad-bash.sh","content":"#!/usr/bin/env bash\n#\n# Example of a poorly-written bash script with common mistakes\n#\n\n# Missing: set -euo pipefail\n\nLOG_FILE=/tmp/example.log\n\n# Function defined after use (will fail)\nmain\n\nlog_info() {\n # Bad: unquoted variable\n echo [INFO] $*\n}\n\n# Bad: using backticks instead of $()\nresult=`date`\n\nprocess_file() {\n file=$1 # Not local\n\n # Bad: not quoting variable\n if [ ! -f $file ]; then\n echo \"File not found\"\n return 1\n fi\n\n # Bad: useless use of cat\n cat $file | grep pattern\n\n # Bad: eval with variable (security risk)\n eval $user_command\n}\n\nmain() {\n # Bad: not checking if arguments provided\n # Bad: unquoted $@\n for file in $@; do\n # Bad: not checking return value\n cd /some/directory\n rm -rf * # DANGEROUS!\n\n process_file $file\n done\n\n # Bad: checking $? after multiple commands\n if [ $? -eq 0 ]; then\n echo \"Success\"\n fi\n}\n\n# Bad: calling main without \"$@\"\nmain $*","content_type":"application/x-sh; charset=utf-8","language":"bash","size":983,"content_sha256":"1c038b30c82b94e0c7134af20e26c6a7c385993d9a2a27c6188f73e51cdbd542"},{"filename":"examples/bad-shell.sh","content":"#!/bin/sh\n#\n# Example of a poorly-written shell script with bashisms and other mistakes\n#\n\n# Bad: using bash-specific [[ ]]\nif [[ -f /etc/passwd ]]; then\n echo \"File exists\"\nfi\n\n# Bad: using bash arrays in sh script\narray=(one two three)\necho ${array[0]}\n\n# Bad: using bash-specific function keyword\nfunction process_data {\n # Bad: using bash-specific 'local'\n local data=$1\n\n # Bad: unquoted variable\n echo $data\n}\n\n# Bad: using 'source' instead of '.'\nsource /etc/profile\n\n# Bad: using == instead of =\nif [ \"$var\" == \"value\" ]; then\n echo \"match\"\nfi\n\n# Bad: process substitution (bash-specific)\ndiff \u003c(ls dir1) \u003c(ls dir2)\n\n# Bad: brace expansion (bash-specific)\necho {1..10}\n\n# Bad: $RANDOM (bash-specific)\nrandom_num=$RANDOM\n\n# Bad: using [[ with regex (bash-specific)\nif [[ \"$string\" =~ pattern ]]; then\n echo \"matches\"\nfi\n\n# Bad: not quoting variables\nfile=/path/with spaces/file.txt\ncat $file\n\n# Bad: useless use of cat\ncat file.txt | grep pattern\n\n# Bad: using eval without sanitization\neval $user_input","content_type":"application/x-sh; charset=utf-8","language":"bash","size":1031,"content_sha256":"3b5bb2e3690b1236e19b6588cef0c513b02f95202ffe9b9ea89e5f78023259e1"},{"filename":"examples/good-bash.sh","content":"#!/usr/bin/env bash\n#\n# Example of a well-written bash script following best practices\n#\n\nset -euo pipefail\n\n# Constants\nSCRIPT_DIR=\"$(cd \"$(dirname \"${BASH_SOURCE[0]}\")\" && pwd)\"\nreadonly SCRIPT_DIR\nreadonly LOG_FILE=\"/tmp/example.log\"\n\n# Functions\nlog_info() {\n echo \"[INFO] $*\" | tee -a \"$LOG_FILE\"\n}\n\nlog_error() {\n echo \"[ERROR] $*\" >&2 | tee -a \"$LOG_FILE\"\n}\n\ncleanup() {\n log_info \"Cleaning up...\"\n rm -f \"$temp_file\"\n}\n\ntrap cleanup EXIT\n\nprocess_file() {\n local file=\"$1\"\n\n if [[ ! -f \"$file\" ]]; then\n log_error \"File not found: $file\"\n return 1\n fi\n\n log_info \"Processing file: $file\"\n\n # Good: using modern command substitution\n local line_count\n line_count=$(wc -l \u003c \"$file\")\n\n log_info \"File has $line_count lines\"\n return 0\n}\n\nmain() {\n log_info \"Script started from $SCRIPT_DIR\"\n\n # Create temporary file\n local temp_file\n temp_file=$(mktemp)\n\n # Good: proper argument handling\n if [[ $# -eq 0 ]]; then\n log_error \"Usage: $0 \u003cfile1> [file2 ...]\"\n exit 1\n fi\n\n # Good: quoted \"$@\" preserves arguments\n for file in \"$@\"; do\n if ! process_file \"$file\"; then\n log_error \"Failed to process: $file\"\n exit 1\n fi\n done\n\n log_info \"Script completed successfully\"\n}\n\nmain \"$@\"","content_type":"application/x-sh; charset=utf-8","language":"bash","size":1318,"content_sha256":"784a33c2e2ea59537570844933d03a4db6dd3cef24abda0c9b581e9fd7522f66"},{"filename":"examples/good-shell.sh","content":"#!/bin/sh\n#\n# Example of a well-written POSIX shell script\n#\n\nset -eu\n\n# POSIX-compliant - no bashisms\n\nreadonly SCRIPT_NAME=\"${0##*/}\"\nreadonly LOG_FILE=\"/tmp/example.log\"\n\nlog_info() {\n printf '[INFO] %s\\n' \"$*\" | tee -a \"$LOG_FILE\"\n}\n\nlog_error() {\n printf '[ERROR] %s\\n' \"$*\" >&2\n}\n\ncleanup() {\n log_info \"Cleaning up...\"\n rm -f \"$temp_file\"\n}\n\ntrap cleanup EXIT INT TERM\n\nprocess_file() {\n file=\"$1\"\n\n # POSIX: using [ ] not [[ ]]\n if [ ! -f \"$file\" ]; then\n log_error \"File not found: $file\"\n return 1\n fi\n\n log_info \"Processing file: $file\"\n\n # POSIX: command substitution with $()\n line_count=$(wc -l \u003c \"$file\")\n\n log_info \"File has $line_count lines\"\n return 0\n}\n\nmain() {\n log_info \"Script started\"\n\n # Create temporary file\n temp_file=$(mktemp)\n\n # Proper argument handling\n if [ $# -eq 0 ]; then\n log_error \"Usage: $SCRIPT_NAME \u003cfile1> [file2 ...]\"\n exit 1\n fi\n\n # POSIX: iterate over positional parameters\n for file in \"$@\"; do\n if ! process_file \"$file\"; then\n log_error \"Failed to process: $file\"\n exit 1\n fi\n done\n\n log_info \"Script completed successfully\"\n}\n\nmain \"$@\"","content_type":"application/x-sh; charset=utf-8","language":"bash","size":1218,"content_sha256":"424ae00428afbf1030dfa0f7fe66f9a60bc14b982ffdb9a57bfe29d9d4229762"},{"filename":"scripts/run_ci_checks.sh","content":"#!/usr/bin/env bash\n#\n# Deterministic CI runner for bash-script-validator.\n# Uses system shellcheck only and fails fast when unavailable.\n#\n\nset -euo pipefail\n\nSCRIPT_DIR=\"$(cd \"$(dirname \"${BASH_SOURCE[0]}\")\" && pwd)\"\nreadonly SCRIPT_DIR\nTEST_SCRIPT=\"$SCRIPT_DIR/test_validate.sh\"\n\nif ! command -v shellcheck >/dev/null 2>&1; then\n echo \"[ERROR] System shellcheck is required for deterministic CI checks.\" >&2\n echo \" Install shellcheck and re-run scripts/run_ci_checks.sh.\" >&2\n exit 1\nfi\n\necho \"[INFO] Running deterministic bash-script-validator regression suite...\"\nCI=1 \\\nVALIDATOR_REQUIRE_SHELLCHECK=1 \\\nVALIDATOR_SHELLCHECK_MODE=system \\\nbash \"$TEST_SCRIPT\"\n","content_type":"application/x-sh; charset=utf-8","language":"bash","size":681,"content_sha256":"402ca381512cde6de35f1a9fb50225df2d2b06e1441193ef70e207df0cda4bc0"},{"filename":"scripts/shellcheck_wrapper.sh","content":"#!/usr/bin/env bash\n#\n# ShellCheck Wrapper with Temporary Virtual Environment\n#\n# This script creates a temporary Python virtual environment, installs shellcheck-py,\n# runs shellcheck, and cleans up afterwards.\n#\n# Usage: ./shellcheck_wrapper.sh [shellcheck-options] \u003cscript-file>\n#\n\nset -euo pipefail\n\n# Colors\nRED='\\033[0;31m'\nGREEN='\\033[0;32m'\nYELLOW='\\033[1;33m'\nBLUE='\\033[0;34m'\nNC='\\033[0m' # No Color\n\n# Configuration\nVENV_DIR=\"\"\nCLEANUP_ON_EXIT=true\nCACHE_VENV=false\nVENV_CACHE_DIR=\"${HOME}/.cache/bash-script-validator/shellcheck-venv\"\n\n# Cleanup function\ncleanup() {\n if [[ \"$CLEANUP_ON_EXIT\" == \"true\" ]] && [[ -n \"$VENV_DIR\" ]] && [[ -d \"$VENV_DIR\" ]]; then\n echo -e \"${BLUE}[INFO]${NC} Cleaning up temporary virtual environment...\" >&2\n rm -rf \"$VENV_DIR\"\n fi\n}\n\ntrap cleanup EXIT INT TERM\n\n# Check if Python 3 is available\ncheck_python() {\n if ! command -v python3 &>/dev/null; then\n echo -e \"${RED}[ERROR]${NC} python3 not found. Please install Python 3.\" >&2\n exit 1\n fi\n}\n\n# Check if shellcheck is already available system-wide\ncheck_system_shellcheck() {\n if command -v shellcheck &>/dev/null; then\n # ShellCheck is available, use it directly\n shellcheck \"$@\"\n exit $?\n fi\n}\n\n# Create and activate virtual environment\nsetup_venv() {\n if [[ \"$CACHE_VENV\" == \"true\" ]] && [[ -d \"$VENV_CACHE_DIR\" ]]; then\n echo -e \"${GREEN}[INFO]${NC} Using cached virtual environment...\" >&2\n VENV_DIR=\"$VENV_CACHE_DIR\"\n CLEANUP_ON_EXIT=false\n return 0\n fi\n\n echo -e \"${BLUE}[INFO]${NC} Creating temporary virtual environment...\" >&2\n\n if [[ \"$CACHE_VENV\" == \"true\" ]]; then\n mkdir -p \"$(dirname \"$VENV_CACHE_DIR\")\"\n VENV_DIR=\"$VENV_CACHE_DIR\"\n CLEANUP_ON_EXIT=false\n else\n VENV_DIR=$(mktemp -d -t shellcheck-venv.XXXXXX)\n fi\n\n python3 -m venv \"$VENV_DIR\"\n\n # Activate virtual environment\n # shellcheck source=/dev/null\n source \"$VENV_DIR/bin/activate\"\n}\n\n# Install shellcheck-py\ninstall_shellcheck() {\n local marker_file=\"$VENV_DIR/.shellcheck_installed\"\n\n if [[ -f \"$marker_file\" ]]; then\n echo -e \"${GREEN}[INFO]${NC} ShellCheck already installed in cached venv\" >&2\n return 0\n fi\n\n echo -e \"${BLUE}[INFO]${NC} Installing shellcheck-py...\" >&2\n\n # Upgrade pip first (suppress output)\n pip3 install --upgrade pip &>/dev/null\n\n # Install shellcheck-py\n if pip3 install shellcheck-py &>/dev/null; then\n echo -e \"${GREEN}[INFO]${NC} ShellCheck installed successfully\" >&2\n touch \"$marker_file\"\n return 0\n else\n echo -e \"${RED}[ERROR]${NC} Failed to install shellcheck-py\" >&2\n return 1\n fi\n}\n\n# Run shellcheck\nrun_shellcheck() {\n if [[ ! -f \"$VENV_DIR/bin/shellcheck\" ]]; then\n echo -e \"${RED}[ERROR]${NC} ShellCheck binary not found in virtual environment\" >&2\n exit 1\n fi\n\n # Run shellcheck with all provided arguments\n \"$VENV_DIR/bin/shellcheck\" \"$@\"\n}\n\n# Main function\nmain() {\n # If no arguments, show usage\n if [[ $# -eq 0 ]]; then\n echo \"Usage: $0 [--cache] [--no-cache] [shellcheck-options] \u003cscript-file>\"\n echo \"\"\n echo \"Options:\"\n echo \" --cache Cache the virtual environment for faster subsequent runs\"\n echo \" --no-cache Don't use cached venv (default)\"\n echo \" --clear-cache Clear the cached virtual environment\"\n echo \"\"\n echo \"Examples:\"\n echo \" $0 script.sh\"\n echo \" $0 --cache -s bash script.sh\"\n echo \" $0 -f gcc script.sh\"\n exit 0\n fi\n\n # Parse wrapper-specific options\n local args=()\n while [[ $# -gt 0 ]]; do\n case \"$1\" in\n --cache)\n CACHE_VENV=true\n shift\n ;;\n --no-cache)\n CACHE_VENV=false\n shift\n ;;\n --clear-cache)\n if [[ -d \"$VENV_CACHE_DIR\" ]]; then\n echo -e \"${BLUE}[INFO]${NC} Clearing cached virtual environment...\"\n rm -rf \"$VENV_CACHE_DIR\"\n echo -e \"${GREEN}[INFO]${NC} Cache cleared\"\n else\n echo -e \"${YELLOW}[INFO]${NC} No cache to clear\"\n fi\n exit 0\n ;;\n *)\n args+=(\"$1\")\n shift\n ;;\n esac\n done\n\n # Check if system shellcheck is available\n check_system_shellcheck \"${args[@]}\"\n\n # System shellcheck not found, use venv approach\n check_python\n setup_venv\n install_shellcheck\n\n # Run shellcheck with remaining arguments\n run_shellcheck \"${args[@]}\"\n}\n\nmain \"$@\"","content_type":"application/x-sh; charset=utf-8","language":"bash","size":4739,"content_sha256":"05847446c8291a8514c1c8ece2fd494e02ab7a09919b4d21782dcf8733a4c30b"},{"filename":"scripts/test_validate.sh","content":"#!/usr/bin/env bash\n#\n# Regression test suite for validate.sh\n#\n# Runs the validator against each example file and asserts the expected exit code.\n# Exit 0 when all assertions pass; non-zero otherwise.\n#\n\nset -euo pipefail\n\nSCRIPT_DIR=\"$(cd \"$(dirname \"${BASH_SOURCE[0]}\")\" && pwd)\"\nreadonly SCRIPT_DIR\nVALIDATOR=\"$SCRIPT_DIR/validate.sh\"\nEXAMPLES_DIR=\"$SCRIPT_DIR/../examples\"\nTMP_DIR=\"$(mktemp -d)\"\nFAKE_SHELLCHECK_BIN=\"$TMP_DIR/fake-shellcheck-bin\"\n\ncleanup() {\n rm -rf \"$TMP_DIR\"\n}\ntrap cleanup EXIT\n\n# Counters\nPASS=0\nFAIL=0\n\n# ─── helpers ────────────────────────────────────────────────────────────────\n\npass() { echo \" PASS: $1\"; PASS=$((PASS + 1)); }\nfail() { echo \" FAIL: $1\"; FAIL=$((FAIL + 1)); }\n\n# Create a deterministic shellcheck stub so regression tests do not depend on host tooling.\nmkdir -p \"$FAKE_SHELLCHECK_BIN\"\ncat > \"$FAKE_SHELLCHECK_BIN/shellcheck\" \u003c\u003c'EOF'\n#!/usr/bin/env bash\nset -euo pipefail\n\nmode=\"${FAKE_SHELLCHECK_MODE:-ok}\"\ntarget=\"${@: -1}\"\n\ncase \"$mode\" in\n ok)\n exit 0\n ;;\n warning)\n echo \"${target}:1:1: warning: fake shellcheck warning [SC9999]\"\n exit 1\n ;;\n infra)\n echo \"fake shellcheck infrastructure failure\" >&2\n exit 3\n ;;\n *)\n echo \"unknown FAKE_SHELLCHECK_MODE: $mode\" >&2\n exit 2\n ;;\nesac\nEOF\nchmod +x \"$FAKE_SHELLCHECK_BIN/shellcheck\"\n\n# Run the validator and return its exit code without aborting this script.\n# Uses || to prevent set -e from treating a non-zero validator exit as fatal.\nrun_validator() {\n local file=\"$1\"\n shift\n\n local exit_code=0\n if [[ $# -gt 0 ]]; then\n env \"$@\" bash \"$VALIDATOR\" \"$file\" >/dev/null 2>&1 || exit_code=$?\n else\n bash \"$VALIDATOR\" \"$file\" >/dev/null 2>&1 || exit_code=$?\n fi\n echo \"$exit_code\"\n}\n\nvalidator_output() {\n local file=\"$1\"\n shift\n\n if [[ $# -gt 0 ]]; then\n env \"$@\" bash \"$VALIDATOR\" \"$file\" 2>&1 || true\n else\n bash \"$VALIDATOR\" \"$file\" 2>&1 || true\n fi\n}\n\n# Assert that the validator exits with a specific code for the given file.\nassert_exit_code() {\n local label=\"$1\"\n local file=\"$2\"\n local expected=\"$3\"\n shift 3\n\n local actual\n actual=$(run_validator \"$file\" \"$@\")\n\n if [[ \"$actual\" -eq \"$expected\" ]]; then\n pass \"$label (exit $actual)\"\n else\n fail \"$label — expected exit $expected, got $actual\"\n # Re-run with output visible so the failure is diagnosable.\n echo \" --- validator output ---\"\n validator_output \"$file\" \"$@\" | sed 's/^/ /'\n echo \" --- end output ---\"\n fi\n}\n\nassert_exit_code_in() {\n local label=\"$1\"\n local file=\"$2\"\n local expected_csv=\"$3\"\n shift 3\n\n local actual\n actual=$(run_validator \"$file\" \"$@\")\n\n if [[ \",$expected_csv,\" == *\",$actual,\"* ]]; then\n pass \"$label (exit $actual)\"\n else\n fail \"$label — expected one of [$expected_csv], got $actual\"\n echo \" --- validator output ---\"\n validator_output \"$file\" \"$@\" | sed 's/^/ /'\n echo \" --- end output ---\"\n fi\n}\n\n# Assert that a pattern IS found in the validator output for a given file.\nassert_output_contains() {\n local label=\"$1\"\n local file=\"$2\"\n local pattern=\"$3\"\n shift 3\n\n local output\n output=$(validator_output \"$file\" \"$@\")\n\n if echo \"$output\" | grep -qE \"$pattern\"; then\n pass \"$label\"\n else\n fail \"$label — pattern not found: $pattern\"\n echo \" --- validator output ---\"\n echo \"$output\" | sed 's/^/ /'\n echo \" --- end output ---\"\n fi\n}\n\n# Assert that a pattern is NOT found in the validator output for a given file.\nassert_output_not_contains() {\n local label=\"$1\"\n local file=\"$2\"\n local pattern=\"$3\"\n shift 3\n\n local output\n output=$(validator_output \"$file\" \"$@\")\n\n if echo \"$output\" | grep -qE \"$pattern\"; then\n fail \"$label — unexpected pattern found: $pattern\"\n echo \" --- validator output ---\"\n echo \"$output\" | sed 's/^/ /'\n echo \" --- end output ---\"\n else\n pass \"$label\"\n fi\n}\n\nassert_shellcheck_stage_verifiable() {\n local label=\"$1\"\n local file=\"$2\"\n shift 2\n\n local output\n output=$(validator_output \"$file\" \"$@\")\n\n if ! echo \"$output\" | grep -q '\\[SHELLCHECK\\]'; then\n fail \"$label — missing [SHELLCHECK] stage\"\n echo \" --- validator output ---\"\n echo \"$output\" | sed 's/^/ /'\n echo \" --- end output ---\"\n return\n fi\n\n if echo \"$output\" | grep -qE 'ShellCheck unavailable|ShellCheck not installed'; then\n fail \"$label — ShellCheck stage unavailable\"\n echo \" --- validator output ---\"\n echo \"$output\" | sed 's/^/ /'\n echo \" --- end output ---\"\n return\n fi\n\n if echo \"$output\" | grep -qE 'No ShellCheck issues found|SC[0-9]{4}|: (error|warning|note|style):'; then\n pass \"$label\"\n return\n fi\n\n fail \"$label — ShellCheck produced no verifiable signal\"\n echo \" --- validator output ---\"\n echo \"$output\" | sed 's/^/ /'\n echo \" --- end output ---\"\n}\n\n# ─── test cases ─────────────────────────────────────────────────────────────\n\necho \"Running bash-script-validator tests...\"\necho \"\"\n\n# --- good-bash.sh: well-written bash, must exit 0 ---\necho \"[good-bash.sh]\"\nassert_exit_code \\\n \"exits cleanly (code 0)\" \\\n \"$EXAMPLES_DIR/good-bash.sh\" \\\n 0\n\nassert_output_not_contains \\\n \"no false-positive errors\" \\\n \"$EXAMPLES_DIR/good-bash.sh\" \\\n \"✗\"\n\nassert_shellcheck_stage_verifiable \\\n \"shellcheck stage is verifiable\" \\\n \"$EXAMPLES_DIR/good-bash.sh\"\n\n# --- good-shell.sh: well-written POSIX sh, must exit 0 ---\necho \"\"\necho \"[good-shell.sh]\"\nassert_exit_code \\\n \"exits cleanly (code 0)\" \\\n \"$EXAMPLES_DIR/good-shell.sh\" \\\n 0\n\nassert_output_not_contains \\\n \"no false-positive [[ ]] error from comment on line 31\" \\\n \"$EXAMPLES_DIR/good-shell.sh\" \\\n \"\\[\\[\"\n\nassert_output_not_contains \\\n \"no false-positive errors\" \\\n \"$EXAMPLES_DIR/good-shell.sh\" \\\n \"✗\"\n\nassert_shellcheck_stage_verifiable \\\n \"shellcheck stage is verifiable\" \\\n \"$EXAMPLES_DIR/good-shell.sh\"\n\n# --- bad-bash.sh: intentionally bad bash, must be non-clean ---\necho \"\"\necho \"[bad-bash.sh]\"\nassert_exit_code_in \\\n \"exits non-clean (code 1 or 2)\" \\\n \"$EXAMPLES_DIR/bad-bash.sh\" \\\n \"1,2\"\n\nassert_output_contains \\\n \"detects eval with variable\" \\\n \"$EXAMPLES_DIR/bad-bash.sh\" \\\n \"eval with variable\"\n\nassert_output_contains \\\n \"detects useless cat\" \\\n \"$EXAMPLES_DIR/bad-bash.sh\" \\\n \"Useless use of cat\"\n\n# --- bad-shell.sh: intentionally bad POSIX sh, must exit 2 ---\necho \"\"\necho \"[bad-shell.sh]\"\nassert_exit_code \\\n \"exits with errors (code 2)\" \\\n \"$EXAMPLES_DIR/bad-shell.sh\" \\\n 2\n\nassert_output_contains \\\n \"detects [[ ]] in sh script (line 7, actual code)\" \\\n \"$EXAMPLES_DIR/bad-shell.sh\" \\\n \"Bash-specific \\[\\[ \\]\\]\"\n\nassert_output_contains \\\n \"detects bash arrays in sh script\" \\\n \"$EXAMPLES_DIR/bad-shell.sh\" \\\n \"Bash-specific arrays\"\n\nassert_output_contains \\\n \"detects function keyword in sh script\" \\\n \"$EXAMPLES_DIR/bad-shell.sh\" \\\n \"function.*keyword\"\n\nassert_output_contains \\\n \"detects source command in sh script\" \\\n \"$EXAMPLES_DIR/bad-shell.sh\" \\\n \"source.*command\"\n\nassert_output_contains \\\n \"detects eval with variable\" \\\n \"$EXAMPLES_DIR/bad-shell.sh\" \\\n \"eval with variable\"\n\nassert_output_contains \\\n \"detects useless cat\" \\\n \"$EXAMPLES_DIR/bad-shell.sh\" \\\n \"Useless use of cat\"\n\n# Only real code lines flagged — not comment lines for [[ check\nassert_output_not_contains \\\n \"[[ check does not flag comment lines in bad-shell.sh\" \\\n \"$EXAMPLES_DIR/bad-shell.sh\" \\\n \"Line [0-9]*:# Bad: using bash-specific\"\n\n# --- p1 regression fixtures ---\nINDENTED_SH=\"$TMP_DIR/indented-bashisms.sh\"\ncat > \"$INDENTED_SH\" \u003c\u003c'EOF'\n#!/bin/sh\nset -e\n function helper {\n echo \"bad\"\n }\n source /etc/profile\nhelper\nEOF\n\nCOMMENT_ONLY_ERR_HANDLING=\"$TMP_DIR/comment-only-error-handling.sh\"\ncat > \"$COMMENT_ONLY_ERR_HANDLING\" \u003c\u003c'EOF'\n#!/bin/sh\n# set -e\n# set -o errexit\n# trap 'echo failed' ERR\necho \"hello\"\nEOF\n\necho \"\"\necho \"[p1 regressions]\"\nassert_output_contains \\\n \"indented function in sh is detected\" \\\n \"$INDENTED_SH\" \\\n \"function.*keyword\"\n\nassert_output_contains \\\n \"indented source in sh is detected\" \\\n \"$INDENTED_SH\" \\\n \"source.*command\"\n\nassert_output_contains \\\n \"comment-only set -e does not suppress warning\" \\\n \"$COMMENT_ONLY_ERR_HANDLING\" \\\n \"Consider adding error handling\"\n\n# --- p2 deterministic shellcheck stage regressions ---\necho \"\"\necho \"[p2 shellcheck-mode regressions]\"\n\nassert_exit_code \\\n \"system mode accepts deterministic clean shellcheck run\" \\\n \"$EXAMPLES_DIR/good-bash.sh\" \\\n 0 \\\n VALIDATOR_REQUIRE_SHELLCHECK=1 \\\n VALIDATOR_SHELLCHECK_MODE=system \\\n PATH=\"$FAKE_SHELLCHECK_BIN:/usr/bin:/bin\"\n\nassert_output_contains \\\n \"system mode reports clean shellcheck output\" \\\n \"$EXAMPLES_DIR/good-bash.sh\" \\\n \"No ShellCheck issues found\" \\\n VALIDATOR_REQUIRE_SHELLCHECK=1 \\\n VALIDATOR_SHELLCHECK_MODE=system \\\n PATH=\"$FAKE_SHELLCHECK_BIN:/usr/bin:/bin\"\n\nassert_exit_code \\\n \"system mode returns warning on shellcheck findings\" \\\n \"$EXAMPLES_DIR/good-bash.sh\" \\\n 1 \\\n VALIDATOR_REQUIRE_SHELLCHECK=1 \\\n VALIDATOR_SHELLCHECK_MODE=system \\\n FAKE_SHELLCHECK_MODE=warning \\\n PATH=\"$FAKE_SHELLCHECK_BIN:/usr/bin:/bin\"\n\nassert_output_contains \\\n \"system mode surfaces shellcheck issue codes\" \\\n \"$EXAMPLES_DIR/good-bash.sh\" \\\n \"SC9999\" \\\n VALIDATOR_REQUIRE_SHELLCHECK=1 \\\n VALIDATOR_SHELLCHECK_MODE=system \\\n FAKE_SHELLCHECK_MODE=warning \\\n PATH=\"$FAKE_SHELLCHECK_BIN:/usr/bin:/bin\"\n\nassert_exit_code \\\n \"system mode fails on shellcheck infrastructure error\" \\\n \"$EXAMPLES_DIR/good-bash.sh\" \\\n 2 \\\n VALIDATOR_REQUIRE_SHELLCHECK=1 \\\n VALIDATOR_SHELLCHECK_MODE=system \\\n FAKE_SHELLCHECK_MODE=infra \\\n PATH=\"$FAKE_SHELLCHECK_BIN:/usr/bin:/bin\"\n\nassert_output_contains \\\n \"system mode reports shellcheck infrastructure error\" \\\n \"$EXAMPLES_DIR/good-bash.sh\" \\\n \"ShellCheck execution failed \\(exit 3\\)\" \\\n VALIDATOR_REQUIRE_SHELLCHECK=1 \\\n VALIDATOR_SHELLCHECK_MODE=system \\\n FAKE_SHELLCHECK_MODE=infra \\\n PATH=\"$FAKE_SHELLCHECK_BIN:/usr/bin:/bin\"\n\nassert_exit_code \\\n \"invalid shellcheck mode exits with error\" \\\n \"$EXAMPLES_DIR/good-bash.sh\" \\\n 2 \\\n VALIDATOR_SHELLCHECK_MODE=invalid-mode\n\nassert_output_contains \\\n \"invalid shellcheck mode prints actionable error\" \\\n \"$EXAMPLES_DIR/good-bash.sh\" \\\n \"Invalid VALIDATOR_SHELLCHECK_MODE\" \\\n VALIDATOR_SHELLCHECK_MODE=invalid-mode\n\n# --- edge cases ---\necho \"\"\necho \"[edge cases]\"\n\n# P0: strict mode must fail when ShellCheck is unavailable\nassert_exit_code \\\n \"strict mode fails when shellcheck is unavailable\" \\\n \"$EXAMPLES_DIR/good-bash.sh\" \\\n 2 \\\n VALIDATOR_REQUIRE_SHELLCHECK=1 \\\n VALIDATOR_DISABLE_SHELLCHECK=1\n\nassert_output_contains \\\n \"strict mode reports shellcheck unavailable\" \\\n \"$EXAMPLES_DIR/good-bash.sh\" \\\n \"ShellCheck (unavailable|disabled by configuration)\" \\\n VALIDATOR_REQUIRE_SHELLCHECK=1 \\\n VALIDATOR_DISABLE_SHELLCHECK=1\n\n# Missing file → exit 1 from the validator's own error path\nassert_exit_code \\\n \"missing file exits non-zero\" \\\n \"/nonexistent/path/script.sh\" \\\n 1\n\n# ─── summary ────────────────────────────────────────────────────────────────\n\necho \"\"\necho \"Results: $PASS passed, $FAIL failed\"\necho \"\"\n\nif [[ $FAIL -gt 0 ]]; then\n exit 1\nfi\n","content_type":"application/x-sh; charset=utf-8","language":"bash","size":12096,"content_sha256":"1e098ad2a464826c45259a37c7d185d09cb53be447f9b463c6b353efb4ff04d3"},{"filename":"scripts/validate.sh","content":"#!/usr/bin/env bash\n#\n# Bash/Shell Script Validator\n# Validates bash and shell scripts for syntax errors, best practices, security issues, and optimizations\n#\n\nset -euo pipefail\n\n# Colors for output\nRED='\\033[0;31m'\nYELLOW='\\033[1;33m'\nGREEN='\\033[0;32m'\nBLUE='\\033[0;34m'\nNC='\\033[0m' # No Color\n\n# Counters\nERROR_COUNT=0\nWARNING_COUNT=0\nINFO_COUNT=0\nSTYLE_COUNT=0\n\n# Script path\nSCRIPT_DIR=\"$(cd \"$(dirname \"${BASH_SOURCE[0]}\")\" && pwd)\"\n\n# ShellCheck policy:\n# - default strict in CI\n# - override with VALIDATOR_REQUIRE_SHELLCHECK=0|1\nREQUIRE_SHELLCHECK=\"${VALIDATOR_REQUIRE_SHELLCHECK:-}\"\nif [[ -z \"$REQUIRE_SHELLCHECK\" ]]; then\n if [[ -n \"${CI:-}\" ]]; then\n REQUIRE_SHELLCHECK=1\n else\n REQUIRE_SHELLCHECK=0\n fi\nfi\n\n# ShellCheck provider selection:\n# - auto (default): system shellcheck, then wrapper fallback\n# - system: require system shellcheck in PATH\n# - wrapper: require wrapper script\n# - disabled: skip ShellCheck stage\nSHELLCHECK_MODE=\"${VALIDATOR_SHELLCHECK_MODE:-auto}\"\nif [[ \"${VALIDATOR_DISABLE_SHELLCHECK:-0}\" == \"1\" ]]; then\n SHELLCHECK_MODE=\"disabled\"\nfi\n\nusage() {\n cat \u003c\u003cEOF\nUsage: $0 \u003cscript-file>\n\nValidates bash and shell scripts for:\n - Syntax errors\n - ShellCheck warnings\n - Security issues\n - Performance optimizations\n - Portability concerns\n\nOptions:\n -h, --help Show this help message\n\nExamples:\n $0 myscript.sh\n $0 /path/to/script.bash\nEOF\n exit 0\n}\n\nprint_header() {\n echo -e \"${BLUE}========================================${NC}\"\n echo -e \"${BLUE}$1${NC}\"\n echo -e \"${BLUE}========================================${NC}\"\n}\n\nprint_section() {\n echo \"\"\n echo -e \"${BLUE}[$1]${NC}\"\n}\n\nprint_error() {\n echo -e \"${RED}✗ $1${NC}\"\n ((ERROR_COUNT++))\n}\n\nprint_warning() {\n echo -e \"${YELLOW}⚠ $1${NC}\"\n ((WARNING_COUNT++))\n}\n\nprint_info() {\n echo -e \"${BLUE}ℹ $1${NC}\"\n ((INFO_COUNT++))\n}\n\nprint_success() {\n echo -e \"${GREEN}✓ $1${NC}\"\n}\n\n# Detect shell type from shebang (returns shell:status format)\ndetect_shell() {\n local file=\"$1\"\n local shebang\n\n shebang=$(head -n 1 \"$file\")\n\n # Check if shebang is present\n if [[ ! \"$shebang\" =~ ^#! ]]; then\n echo \"bash:no-shebang\"\n return\n fi\n\n if [[ \"$shebang\" =~ ^#!.*bash ]]; then\n echo \"bash\"\n elif [[ \"$shebang\" =~ ^#!/bin/sh([[:space:]]|$) ]] || \\\n [[ \"$shebang\" =~ ^#!/usr/bin/sh([[:space:]]|$) ]] || \\\n [[ \"$shebang\" =~ /env[[:space:]]+sh([[:space:]]|$) ]]; then\n echo \"sh\"\n elif [[ \"$shebang\" =~ ^#!.*zsh ]]; then\n echo \"zsh\"\n elif [[ \"$shebang\" =~ ^#!.*ksh ]]; then\n echo \"ksh\"\n elif [[ \"$shebang\" =~ ^#!.*dash ]]; then\n echo \"dash\"\n else\n # Unknown shebang\n echo \"bash:unknown-shebang:$shebang\"\n fi\n}\n\n# Run syntax validation\nvalidate_syntax() {\n local file=\"$1\"\n local shell_type=\"$2\"\n\n print_section \"SYNTAX CHECK\"\n\n case \"$shell_type\" in\n bash)\n if bash -n \"$file\" 2>/dev/null; then\n print_success \"No syntax errors found (bash -n)\"\n return 0\n else\n local errors\n errors=$(bash -n \"$file\" 2>&1)\n print_error \"Syntax errors found:\"\n echo \"$errors\" | sed 's/^/ /'\n return 1\n fi\n ;;\n sh|dash)\n if sh -n \"$file\" 2>/dev/null; then\n print_success \"No syntax errors found (sh -n)\"\n return 0\n else\n local errors\n errors=$(sh -n \"$file\" 2>&1)\n print_error \"Syntax errors found:\"\n echo \"$errors\" | sed 's/^/ /'\n return 1\n fi\n ;;\n *)\n print_info \"Syntax check skipped for shell type: $shell_type\"\n return 0\n ;;\n esac\n}\n\n# Run shellcheck validation\nrun_shellcheck() {\n local file=\"$1\"\n local shell_type=\"$2\"\n local shellcheck_mode=\"$SHELLCHECK_MODE\"\n\n print_section \"SHELLCHECK\"\n\n local shell_name=\"\"\n case \"$shell_type\" in\n bash) shell_name=\"bash\" ;;\n sh|dash) shell_name=\"sh\" ;;\n zsh) shell_name=\"zsh\" ;;\n ksh) shell_name=\"ksh\" ;;\n esac\n\n # Determine which shellcheck to use\n local -a shellcheck_cmd=()\n local unavailable_reason=\"ShellCheck unavailable\"\n\n case \"$shellcheck_mode\" in\n auto)\n if command -v shellcheck &>/dev/null; then\n shellcheck_cmd=(\"shellcheck\")\n elif [[ -x \"$SCRIPT_DIR/shellcheck_wrapper.sh\" ]]; then\n shellcheck_cmd=(\"$SCRIPT_DIR/shellcheck_wrapper.sh\" \"--cache\")\n fi\n ;;\n system)\n if command -v shellcheck &>/dev/null; then\n shellcheck_cmd=(\"shellcheck\")\n else\n unavailable_reason=\"System shellcheck required (VALIDATOR_SHELLCHECK_MODE=system) but not found in PATH\"\n fi\n ;;\n wrapper)\n if [[ -x \"$SCRIPT_DIR/shellcheck_wrapper.sh\" ]]; then\n shellcheck_cmd=(\"$SCRIPT_DIR/shellcheck_wrapper.sh\" \"--cache\")\n else\n unavailable_reason=\"Shellcheck wrapper required (VALIDATOR_SHELLCHECK_MODE=wrapper) but scripts/shellcheck_wrapper.sh is missing or not executable\"\n fi\n ;;\n disabled)\n unavailable_reason=\"ShellCheck disabled by configuration\"\n ;;\n *)\n print_error \"Invalid VALIDATOR_SHELLCHECK_MODE='$shellcheck_mode'. Use one of: auto, system, wrapper, disabled\"\n return 2\n ;;\n esac\n\n if [[ ${#shellcheck_cmd[@]} -eq 0 ]]; then\n if [[ \"$REQUIRE_SHELLCHECK\" == \"1\" ]]; then\n print_error \"$unavailable_reason; static analysis coverage is required\"\n echo \" Install options:\"\n echo \" 1. System-wide: brew install shellcheck (macOS)\"\n echo \" apt-get install shellcheck (Debian/Ubuntu)\"\n echo \" dnf install shellcheck (Fedora)\"\n echo \" 2. Python venv: pip3 install shellcheck-py\"\n echo \" 3. Wrapper auto-installs when python3 is available\"\n return 2\n fi\n\n print_warning \"$unavailable_reason. Static analysis coverage is partial.\"\n echo \" Install options:\"\n echo \" 1. System-wide: brew install shellcheck (macOS)\"\n echo \" apt-get install shellcheck (Debian/Ubuntu)\"\n echo \" dnf install shellcheck (Fedora)\"\n echo \" 2. Python venv: pip3 install shellcheck-py\"\n echo \" 3. Wrapper auto-installs when python3 is available\"\n return 0\n fi\n\n local -a shellcheck_args=()\n if [[ -n \"$shell_name\" ]]; then\n shellcheck_args+=(\"-s\" \"$shell_name\")\n fi\n\n local output\n local shellcheck_exit=0\n if output=$(\"${shellcheck_cmd[@]}\" \"${shellcheck_args[@]}\" -f gcc \"$file\" 2>&1); then\n print_success \"No ShellCheck issues found\"\n return 0\n else\n shellcheck_exit=$?\n fi\n\n if [[ \"$shellcheck_exit\" -eq 1 ]]; then\n local error_lines warning_lines info_lines style_lines total_lines\n\n error_lines=$(echo \"$output\" | grep -c \": error:\" || true)\n warning_lines=$(echo \"$output\" | grep -c \": warning:\" || true)\n info_lines=$(echo \"$output\" | grep -c \": note:\" || true)\n style_lines=$(echo \"$output\" | grep -c \": style:\" || true)\n total_lines=$((error_lines + warning_lines + info_lines + style_lines))\n\n ERROR_COUNT=$((ERROR_COUNT + error_lines))\n WARNING_COUNT=$((WARNING_COUNT + warning_lines))\n INFO_COUNT=$((INFO_COUNT + info_lines))\n STYLE_COUNT=$((STYLE_COUNT + style_lines))\n\n # Keep validator non-green even if output format is unexpected.\n if [[ \"$total_lines\" -eq 0 ]]; then\n print_warning \"ShellCheck reported issues, but no severity markers were parsed\"\n fi\n\n echo \"$output\"\n echo \"\"\n print_info \"See https://www.shellcheck.net/wiki/ for detailed explanations\"\n return 1\n fi\n\n print_error \"ShellCheck execution failed (exit $shellcheck_exit)\"\n if [[ -n \"$output\" ]]; then\n echo \"$output\"\n fi\n return 2\n}\n\n# Grep for a pattern in a file, excluding comment-only lines.\n# A comment-only line is one whose first non-whitespace character is '#'.\n# Returns 0 with output when non-comment matches exist; 1 with no output otherwise.\n# Usage: grep_code [-E] 'pattern' file\ngrep_code() {\n local ext_flag=\"\"\n if [[ \"${1:-}\" == \"-E\" ]]; then\n ext_flag=\"-E\"\n shift\n fi\n local output\n output=$(grep -n ${ext_flag:+$ext_flag} \"$1\" \"$2\" 2>/dev/null | awk '\n {\n colon = index($0, \":\")\n content = substr($0, colon + 1)\n if (content !~ /^[[:space:]]*#/) print $0\n }')\n if [[ -n \"$output\" ]]; then\n echo \"$output\"\n return 0\n else\n return 1\n fi\n}\n\n# Run custom security and optimization checks\nrun_custom_checks() {\n local file=\"$1\"\n local shell_type=\"$2\"\n\n print_section \"CUSTOM CHECKS\"\n\n local found_issues=0\n\n # Security: Check for eval with variables\n if grep_code -E 'eval.*\\

Bash Script Validator Overview This skill validates Bash and POSIX shell scripts with layered checks: 1. Syntax validation ( or ) 2. ShellCheck static analysis (system binary or wrapper fallback) 3. Custom security, portability, and optimization checks Use the default flow below, then branch to fallbacks only when the environment is constrained. Trigger Guidance Use this skill when the request includes script quality, linting, syntax checking, or shell portability work. Trigger Phrases - "Validate this bash script" - "Lint this file" - "Find security issues in this shell script" - "Why does t…

\"$file\" >/dev/null 2>&1; then\n print_warning \"Potential command injection: eval with variable found\"\n grep_code -E 'eval.*\\

Bash Script Validator Overview This skill validates Bash and POSIX shell scripts with layered checks: 1. Syntax validation ( or ) 2. ShellCheck static analysis (system binary or wrapper fallback) 3. Custom security, portability, and optimization checks Use the default flow below, then branch to fallbacks only when the environment is constrained. Trigger Guidance Use this skill when the request includes script quality, linting, syntax checking, or shell portability work. Trigger Phrases - "Validate this bash script" - "Lint this file" - "Find security issues in this shell script" - "Why does t…

\"$file\" | sed 's/^/ Line /'\n found_issues=1\n fi\n\n # Security: Check for unsafe use of rm -rf\n if grep_code -E '(rm -(rf|fr).*\\$|rm -(rf|fr) /)' \"$file\" >/dev/null 2>&1; then\n print_warning \"Dangerous rm -rf usage detected\"\n grep_code -E '(rm -(rf|fr).*\\$|rm -(rf|fr) /)' \"$file\" | sed 's/^/ Line /'\n found_issues=1\n fi\n\n # Performance: Useless use of cat (UUOC)\n # Match: cat \u003cfilename> | grep/awk/sed\n # Use [^|]+ to match one or more non-pipe characters (the filename)\n if grep_code -E 'cat[[:space:]]+[^|]+[[:space:]]*\\|[[:space:]]*(grep|awk|sed)' \"$file\" >/dev/null 2>&1; then\n print_info \"Useless use of cat detected. Consider using redirection instead:\"\n grep_code -E 'cat[[:space:]]+[^|]+[[:space:]]*\\|[[:space:]]*(grep|awk|sed)' \"$file\" | sed 's/^/ Line /'\n found_issues=1\n fi\n\n # Portability: Bash-specific features in sh scripts\n if [[ \"$shell_type\" == \"sh\" ]]; then\n # Check for [[ ]] (bash-specific)\n if grep_code \"\\[\\[\" \"$file\" >/dev/null 2>&1; then\n print_error \"Bash-specific [[ ]] found in sh script. Use [ ] instead\"\n grep_code \"\\[\\[\" \"$file\" | sed 's/^/ Line /'\n found_issues=1\n fi\n\n # Check for arrays (bash-specific)\n if grep_code -E '(declare -a|array=\\()' \"$file\" >/dev/null 2>&1; then\n print_error \"Bash-specific arrays found in sh script\"\n grep_code -E '(declare -a|array=\\()' \"$file\" | sed 's/^/ Line /'\n found_issues=1\n fi\n\n # Check for function keyword (bash-specific)\n if grep_code -E '^[[:space:]]*function[[:space:]]' \"$file\" >/dev/null 2>&1; then\n print_warning \"Bash-specific 'function' keyword in sh script\"\n grep_code -E '^[[:space:]]*function[[:space:]]' \"$file\" | sed 's/^/ Line /'\n found_issues=1\n fi\n\n # Check for source command (bash-specific, use . instead)\n if grep_code -E '^[[:space:]]*source[[:space:]]' \"$file\" >/dev/null 2>&1; then\n print_warning \"Bash-specific 'source' command in sh script. Use '.' instead\"\n grep_code -E '^[[:space:]]*source[[:space:]]' \"$file\" | sed 's/^/ Line /'\n found_issues=1\n fi\n fi\n\n # Check for missing error handling\n local has_error_handling=0\n\n if grep_code -E '^[[:space:]]*set[[:space:]]+-[[:alpha:]]*e[[:alpha:]]*([[:space:]]|$)' \"$file\" >/dev/null 2>&1; then\n has_error_handling=1\n fi\n if grep_code -E '^[[:space:]]*set[[:space:]]+-o[[:space:]]+errexit([[:space:]]|$)' \"$file\" >/dev/null 2>&1; then\n has_error_handling=1\n fi\n if grep_code -E '^[[:space:]]*trap[[:space:]].*ERR([[:space:]]|$)' \"$file\" >/dev/null 2>&1; then\n has_error_handling=1\n fi\n\n if [[ \"$has_error_handling\" -eq 0 ]]; then\n print_info \"Consider adding error handling (set -e/-o errexit or trap ERR)\"\n found_issues=1\n fi\n\n # Check for missing quotes around variables in dangerous contexts\n if grep_code -E '\\$[A-Za-z_][A-Za-z0-9_]*[[:space:]]*>' \"$file\" >/dev/null 2>&1; then\n print_warning \"Unquoted variables in redirection context\"\n grep_code -E '\\$[A-Za-z_][A-Za-z0-9_]*[[:space:]]*>' \"$file\" | sed 's/^/ Line /'\n found_issues=1\n fi\n\n if [[ $found_issues -eq 0 ]]; then\n print_success \"No custom issues found\"\n fi\n\n return 0\n}\n\n# Print summary\nprint_summary() {\n local file=\"$1\"\n local shell_type=\"$2\"\n\n echo \"\"\n print_header \"VALIDATION SUMMARY\"\n echo \"File: $file\"\n echo \"Detected Shell: $shell_type\"\n echo \"\"\n\n if [[ $ERROR_COUNT -eq 0 && $WARNING_COUNT -eq 0 ]]; then\n print_success \"All checks passed! ✓\"\n else\n echo -e \"${RED}Errors:${NC} $ERROR_COUNT\"\n echo -e \"${YELLOW}Warnings:${NC} $WARNING_COUNT\"\n echo -e \"${BLUE}Info:${NC} $INFO_COUNT\"\n echo -e \"Style: $STYLE_COUNT\"\n fi\n\n echo \"\"\n}\n\n# Main validation function\nvalidate_script() {\n local file=\"$1\"\n\n # Check if file exists\n if [[ ! -f \"$file\" ]]; then\n echo \"Error: File '$file' not found\"\n exit 1\n fi\n\n # Check if file is readable\n if [[ ! -r \"$file\" ]]; then\n echo \"Error: File '$file' is not readable\"\n exit 1\n fi\n\n # Check if file is a text file (not binary)\n # Using file command to detect binary files, or grep -I as fallback\n if command -v file &>/dev/null; then\n local file_type\n file_type=$(file -b --mime-encoding \"$file\")\n if [[ \"$file_type\" == \"binary\" ]]; then\n echo \"Error: File '$file' appears to be a binary file, not a text script\"\n exit 1\n fi\n elif ! grep -qI . \"$file\" 2>/dev/null; then\n # Fallback: grep -I skips binary files, if it fails the file is likely binary\n echo \"Error: File '$file' appears to be a binary file, not a text script\"\n exit 1\n fi\n\n local shell_type_raw shell_type shell_status\n shell_type_raw=$(detect_shell \"$file\")\n\n # Parse shell type and status\n shell_type=\"${shell_type_raw%%:*}\"\n shell_status=\"${shell_type_raw#*:}\"\n\n print_header \"BASH/SHELL SCRIPT VALIDATOR\"\n echo \"File: $file\"\n echo \"Detected Shell: $shell_type\"\n\n # Print warnings for special cases\n if [[ \"$shell_status\" == \"no-shebang\" ]]; then\n print_warning \"No shebang found. Defaulting to bash validation.\"\n elif [[ \"$shell_status\" =~ ^unknown-shebang ]]; then\n local unknown_shebang=\"${shell_status#unknown-shebang:}\"\n print_warning \"Unknown shebang: $unknown_shebang. Defaulting to bash validation.\"\n fi\n\n echo \"\"\n\n # Run validations\n validate_syntax \"$file\" \"$shell_type\" || true\n run_shellcheck \"$file\" \"$shell_type\" || true\n run_custom_checks \"$file\" \"$shell_type\" || true\n\n # Print summary\n print_summary \"$file\" \"$shell_type\"\n\n # Exit with appropriate code\n if [[ $ERROR_COUNT -gt 0 ]]; then\n exit 2\n elif [[ $WARNING_COUNT -gt 0 ]]; then\n exit 1\n else\n exit 0\n fi\n}\n\n# Parse arguments\nif [[ $# -eq 0 ]]; then\n usage\nfi\n\ncase \"${1:-}\" in\n -h|--help)\n usage\n ;;\n *)\n validate_script \"$1\"\n ;;\nesac\n","content_type":"application/x-sh; charset=utf-8","language":"bash","size":15615,"content_sha256":"e379364e06eeb597e386faab5e922e7a79df9f1d2df71d0525c54e1374cfb55b"}],"content_json":{"type":"doc","content":[{"type":"heading","attrs":{"level":1},"content":[{"text":"Bash Script Validator","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Overview","type":"text"}]},{"type":"paragraph","content":[{"text":"This skill validates Bash and POSIX shell scripts with layered checks:","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Syntax validation (","type":"text"},{"text":"bash -n","type":"text","marks":[{"type":"code_inline"}]},{"text":" or ","type":"text"},{"text":"sh -n","type":"text","marks":[{"type":"code_inline"}]},{"text":")","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"ShellCheck static analysis (system binary or wrapper fallback)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Custom security, portability, and optimization checks","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Use the default flow below, then branch to fallbacks only when the environment is constrained.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Trigger Guidance","type":"text"}]},{"type":"paragraph","content":[{"text":"Use this skill when the request includes script quality, linting, syntax checking, or shell portability work.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Trigger Phrases","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"\"Validate this bash script\"","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"\"Lint this ","type":"text"},{"text":".sh","type":"text","marks":[{"type":"code_inline"}]},{"text":" file\"","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"\"Find security issues in this shell script\"","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"\"Why does this script fail ShellCheck?\"","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"\"Make this script POSIX compliant\"","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"\"Review this shell script before CI\"","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Non-Trigger Examples","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"General Linux command questions with no script file","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Kubernetes, Terraform, or pipeline validation tasks that do not involve shell scripts","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Pure prose editing tasks","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Deterministic Execution Model","type":"text"}]},{"type":"paragraph","content":[{"text":"Run commands from this skill directory:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"cd devops-skills-plugin/skills/bash-script-validator","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Step 1: Preflight","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Confirm target path exists and is readable.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Confirm ","type":"text"},{"text":"bash","type":"text","marks":[{"type":"code_inline"}]},{"text":" is available.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Determine whether fixes can be applied directly (write access) or only suggested (read-only).","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Step 2: Run Baseline Validation (Default Path)","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"bash scripts/validate.sh \u003cscript-path>","type":"text"}]},{"type":"paragraph","content":[{"text":"For deterministic stage behavior, set the ShellCheck provider explicitly:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Modes: auto (default), system, wrapper, disabled\nVALIDATOR_SHELLCHECK_MODE=system bash scripts/validate.sh \u003cscript-path>","type":"text"}]},{"type":"paragraph","content":[{"text":"Record:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Detected shell type","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Exit code (","type":"text"},{"text":"0","type":"text","marks":[{"type":"code_inline"}]},{"text":" clean, ","type":"text"},{"text":"1","type":"text","marks":[{"type":"code_inline"}]},{"text":" warnings, ","type":"text"},{"text":"2","type":"text","marks":[{"type":"code_inline"}]},{"text":" errors)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"All reported issue lines and ShellCheck codes (","type":"text"},{"text":"SC####","type":"text","marks":[{"type":"code_inline"}]},{"text":") when present","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Step 3: Load Only Needed References","type":"text"}]},{"type":"paragraph","content":[{"text":"Progressive disclosure by issue type:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"ShellCheck code explanations: ","type":"text"},{"text":"docs/shellcheck-reference.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"General fix patterns and security mistakes: ","type":"text"},{"text":"docs/common-mistakes.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Bash-only behavior: ","type":"text"},{"text":"docs/bash-reference.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"POSIX portability or bashism fixes: ","type":"text"},{"text":"docs/shell-reference.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Text-processing optimization issues: ","type":"text"},{"text":"docs/grep-reference.md","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"docs/awk-reference.md","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"docs/sed-reference.md","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"docs/regex-reference.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" (only when directly relevant)","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Step 4: Provide or Apply Fixes","type":"text"}]},{"type":"paragraph","content":[{"text":"For each issue, include:","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Exact location from validator output (line number and snippet)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Root cause","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Corrected code","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Why the change is safer or more portable","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Subsection-level citation (format below)","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"If the request includes patching files and write access is available, apply fixes in small batches grouped by issue type.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Step 5: Rerun Policy (Mandatory After Changes)","type":"text"}]},{"type":"paragraph","content":[{"text":"After each batch of edits, rerun the validator:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"bash scripts/validate.sh \u003cscript-path>","type":"text"}]},{"type":"paragraph","content":[{"text":"Rerun loop rules:","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Continue until no new errors are introduced.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"If warnings remain by design, document why they are intentionally accepted.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"If constraints prevent full resolution, report unresolved items with a clear next action.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Always report the latest rerun exit code and remaining issue count.","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Fallback Behavior","type":"text"}]},{"type":"paragraph","content":[{"text":"Use these branches only when the default flow cannot run as-is.","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Constraint","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Fallback action","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Reporting requirement","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"shellcheck","type":"text","marks":[{"type":"code_inline"}]},{"text":" missing, wrapper available","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Let ","type":"text"},{"text":"scripts/validate.sh","type":"text","marks":[{"type":"code_inline"}]},{"text":" use ","type":"text"},{"text":"scripts/shellcheck_wrapper.sh --cache","type":"text","marks":[{"type":"code_inline"}]},{"text":" automatically","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"State that wrapper mode was used","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"shellcheck","type":"text","marks":[{"type":"code_inline"}]},{"text":" and wrapper unavailable","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Run syntax + custom checks only (validator does this)","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Explicitly call out reduced coverage and missing ShellCheck analysis","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Python unavailable for wrapper","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Skip wrapper path, keep syntax + custom checks","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"State why ShellCheck could not run","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Target file is read-only","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Provide precise patch suggestions without editing","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Mark response as \"advisory only\"","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Target file missing or unreadable","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Stop and request a valid file path","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Do not fabricate results","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Binary/non-text input","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Stop validation","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Report unsupported input type","type":"text"}]}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Citation Guidance for Fixes","type":"text"}]},{"type":"paragraph","content":[{"text":"Use subsection-level citations for every non-trivial fix.","type":"text"}]},{"type":"paragraph","content":[{"text":"Required citation format:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"text"},"content":[{"text":"Reference: docs/\u003cfile>.md -> \u003cSection> -> \u003cSubsection>","type":"text"}]},{"type":"paragraph","content":[{"text":"Examples:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Reference: docs/common-mistakes.md -> 1. Unquoted Variables -> Solution","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Reference: docs/shellcheck-reference.md -> SC2164: Use || exit After cd","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Reference: docs/shell-reference.md -> POSIX Best Practices -> 5. Avoid Bashisms","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"paragraph","content":[{"text":"Citation rules:","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Cite the most specific section that justifies the fix.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"For ShellCheck findings, include both the ","type":"text"},{"text":"SC####","type":"text","marks":[{"type":"code_inline"}]},{"text":" code and the matching section.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"If no exact subsection exists, cite the closest section and state that the fix is inferred from that guidance.","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Response Template","type":"text"}]},{"type":"paragraph","content":[{"text":"Use this structure for deterministic output:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Validation Results","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Command","type":"text","marks":[{"type":"code_inline"}]},{"text":": ","type":"text"},{"text":"bash scripts/validate.sh \u003cscript-path>","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Detected shell","type":"text","marks":[{"type":"code_inline"}]},{"text":": ","type":"text"},{"text":"\u003cshell>","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Exit code","type":"text","marks":[{"type":"code_inline"}]},{"text":": ","type":"text"},{"text":"\u003ccode>","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Summary","type":"text","marks":[{"type":"code_inline"}]},{"text":": ","type":"text"},{"text":"\u003cerrors> errors, \u003cwarnings> warnings, \u003cinfo> info","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Issue","type":"text","marks":[{"type":"code_inline"}]},{"text":": ","type":"text"},{"text":"\u003cshort label> (Line \u003cn>)","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Problem","type":"text","marks":[{"type":"code_inline"}]},{"text":":","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"\u003cproblematic snippet>","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Fix","type":"text","marks":[{"type":"code_inline"}]},{"text":":","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"\u003ccorrected snippet>","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Why","type":"text","marks":[{"type":"code_inline"}]},{"text":": ","type":"text"},{"text":"\u003cshort explanation>","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Reference","type":"text","marks":[{"type":"code_inline"}]},{"text":": ","type":"text"},{"text":"docs/\u003cfile>.md -> \u003cSection> -> \u003cSubsection>","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Rerun command","type":"text","marks":[{"type":"code_inline"}]},{"text":": ","type":"text"},{"text":"bash scripts/validate.sh \u003cscript-path>","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Exit code after fixes","type":"text","marks":[{"type":"code_inline"}]},{"text":": ","type":"text"},{"text":"\u003ccode>","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Remaining issues","type":"text","marks":[{"type":"code_inline"}]},{"text":": ","type":"text"},{"text":"\u003ccount or none>","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Example Flows","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Fully Automated Environment","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# 1) Baseline validation\nbash scripts/validate.sh examples/bad-bash.sh\n\n# 2) Apply fixes to target script\n# 3) Rerun validation\nbash scripts/validate.sh examples/bad-bash.sh","type":"text"}]},{"type":"paragraph","content":[{"text":"Expected behavior: full syntax + ShellCheck + custom-check coverage, with iterative reruns until stable.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Deterministic CI Gate","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Requires a system shellcheck binary.\nbash scripts/run_ci_checks.sh","type":"text"}]},{"type":"paragraph","content":[{"text":"This runner enforces ","type":"text"},{"text":"VALIDATOR_REQUIRE_SHELLCHECK=1","type":"text","marks":[{"type":"code_inline"}]},{"text":" and ","type":"text"},{"text":"VALIDATOR_SHELLCHECK_MODE=system","type":"text","marks":[{"type":"code_inline"}]},{"text":" so CI fails if the ShellCheck stage is skipped or unavailable.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Constrained Environment (No ShellCheck Runtime)","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# shellcheck unavailable and wrapper cannot run\nbash scripts/validate.sh examples/bad-shell.sh","type":"text"}]},{"type":"paragraph","content":[{"text":"Expected behavior: syntax + custom checks still run. Report reduced coverage and list what must be revalidated once ShellCheck is available.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Validator Script Details","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Scripts","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"scripts/validate.sh","type":"text","marks":[{"type":"code_inline"}]},{"text":": primary validator entrypoint","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"scripts/shellcheck_wrapper.sh","type":"text","marks":[{"type":"code_inline"}]},{"text":": optional ShellCheck fallback using a cached Python virtual environment","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Detection and Ordering","type":"text"}]},{"type":"paragraph","content":[{"text":"Validation order in ","type":"text"},{"text":"scripts/validate.sh","type":"text","marks":[{"type":"code_inline"}]},{"text":":","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"File checks (exists/readable/text)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Shebang-based shell detection","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Syntax check","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"ShellCheck (or fallback/skip behavior)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Custom checks","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Summary with exit code","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Exit Codes","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"0","type":"text","marks":[{"type":"code_inline"}]},{"text":": no issues found","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"1","type":"text","marks":[{"type":"code_inline"}]},{"text":": warnings found","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"2","type":"text","marks":[{"type":"code_inline"}]},{"text":": errors found","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"References","type":"text"}]},{"type":"paragraph","content":[{"text":"Load only what is needed:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"docs/bash-reference.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"docs/shell-reference.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"docs/shellcheck-reference.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"docs/common-mistakes.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"docs/grep-reference.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"docs/awk-reference.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"docs/sed-reference.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"docs/regex-reference.md","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Done Criteria","type":"text"}]},{"type":"paragraph","content":[{"text":"This skill update is complete when all are true:","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Trigger guidance is explicit (positive and non-trigger examples).","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Default workflow is deterministic and ordered.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Fallback behavior is explicit for missing tooling and constrained environments.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Fix explanations include subsection-level citations.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Post-fix rerun policy is mandatory and reported with exit codes.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Documentation supports both fully automated and constrained execution paths.","type":"text"}]}]}]}]},"metadata":{"date":"2026-11-02","name":"bash-script-validator","author":"@skillopedia","source":{"stars":331,"repo_name":"cc-devops-skills","origin_url":"https://github.com/akin-ozer/cc-devops-skills/blob/HEAD/devops-skills-plugin/skills/bash-script-validator/SKILL.md","repo_owner":"akin-ozer","body_sha256":"13059ee80d914a1f24577af583a571bccc0aa520b4115b2c4baba4809ac7d33e","cluster_key":"83bcae1a3bbf43c967f39647cf7319c4f59e5fe5a7655bb402fc79dbd278e92b","clean_bundle":{"format":"clean-skill-bundle-v1","source":"akin-ozer/cc-devops-skills/devops-skills-plugin/skills/bash-script-validator/SKILL.md","text_bytes":107335,"source_commit":"276af751e659315aaf56d3ad13d7c26f4e72e28a","skills_sh_rank":16097,"attachment_count":16,"text_attachments":16,"attachment_storage":"skill_attachments","binary_attachments":0},"cluster_size":1,"skill_md_path":"devops-skills-plugin/skills/bash-script-validator/SKILL.md","import_metadata":{"date":"2026-11-02","author":"@skillopedia","version":"v1","category":"testing-qa","category_label":"Testing"},"exact_dupes_collapsed_into_this":0},"version":"v1","category":"testing-qa","import_tag":"skills-sh-top20k-2026-11","description":"Validate, lint, audit, or fix bash/shell/.sh scripts via ShellCheck."}},"renderedAt":1793670929757}

Bash Script Validator Overview This skill validates Bash and POSIX shell scripts with layered checks: 1. Syntax validation ( or ) 2. ShellCheck static analysis (system binary or wrapper fallback) 3. Custom security, portability, and optimization checks Use the default flow below, then branch to fallbacks only when the environment is constrained. Trigger Guidance Use this skill when the request includes script quality, linting, syntax checking, or shell portability work. Trigger Phrases - "Validate this bash script" - "Lint this file" - "Find security issues in this shell script" - "Why does t…