Initial commit
This commit is contained in:
1364
skills/bash-master/references/best_practices.md
Normal file
1364
skills/bash-master/references/best_practices.md
Normal file
File diff suppressed because it is too large
Load Diff
992
skills/bash-master/references/patterns_antipatterns.md
Normal file
992
skills/bash-master/references/patterns_antipatterns.md
Normal file
@@ -0,0 +1,992 @@
|
||||
# Common Bash Patterns and Anti-Patterns
|
||||
|
||||
Collection of proven patterns and common mistakes in bash scripting with explanations and solutions.
|
||||
|
||||
---
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [Variable Handling](#variable-handling)
|
||||
2. [Command Execution](#command-execution)
|
||||
3. [File Operations](#file-operations)
|
||||
4. [String Processing](#string-processing)
|
||||
5. [Arrays and Loops](#arrays-and-loops)
|
||||
6. [Conditionals and Tests](#conditionals-and-tests)
|
||||
7. [Functions](#functions)
|
||||
8. [Error Handling](#error-handling)
|
||||
9. [Process Management](#process-management)
|
||||
10. [Security Patterns](#security-patterns)
|
||||
|
||||
---
|
||||
|
||||
## 🚨 CRITICAL GUIDELINES
|
||||
|
||||
### Windows File Path Requirements
|
||||
|
||||
**MANDATORY: Always Use Backslashes on Windows for File Paths**
|
||||
|
||||
When using Edit or Write tools on Windows, you MUST use backslashes (`\`) in file paths, NOT forward slashes (`/`).
|
||||
|
||||
**Examples:**
|
||||
- ❌ WRONG: `D:/repos/project/file.tsx`
|
||||
- ✅ CORRECT: `D:\repos\project\file.tsx`
|
||||
|
||||
This applies to:
|
||||
- Edit tool file_path parameter
|
||||
- Write tool file_path parameter
|
||||
- All file operations on Windows systems
|
||||
|
||||
|
||||
### Documentation Guidelines
|
||||
|
||||
**NEVER create new documentation files unless explicitly requested by the user.**
|
||||
|
||||
- **Priority**: Update existing README.md files rather than creating new documentation
|
||||
- **Repository cleanliness**: Keep repository root clean - only README.md unless user requests otherwise
|
||||
- **Style**: Documentation should be concise, direct, and professional - avoid AI-generated tone
|
||||
- **User preference**: Only create additional .md files when user specifically asks for documentation
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Variable Handling
|
||||
|
||||
### Pattern: Safe Variable Expansion
|
||||
|
||||
```bash
|
||||
# ✓ GOOD: Always quote variables
|
||||
echo "$variable"
|
||||
cp "$source" "$destination"
|
||||
rm -rf "$directory"
|
||||
|
||||
# ✗ BAD: Unquoted variables
|
||||
echo $variable # Word splitting and globbing
|
||||
cp $source $destination # Breaks with spaces
|
||||
rm -rf $directory # VERY DANGEROUS unquoted
|
||||
```
|
||||
|
||||
**Why:** Unquoted variables undergo word splitting and pathname expansion, leading to unexpected behavior.
|
||||
|
||||
### Pattern: Default Values
|
||||
|
||||
```bash
|
||||
# ✓ GOOD: Use parameter expansion for defaults
|
||||
timeout="${TIMEOUT:-30}"
|
||||
config="${CONFIG_FILE:-$HOME/.config/app.conf}"
|
||||
|
||||
# ✗ BAD: Manual check
|
||||
if [ -z "$TIMEOUT" ]; then
|
||||
timeout=30
|
||||
else
|
||||
timeout="$TIMEOUT"
|
||||
fi
|
||||
```
|
||||
|
||||
**Why:** Parameter expansion is concise, readable, and handles edge cases correctly.
|
||||
|
||||
### Anti-Pattern: Confusing Assignment and Comparison
|
||||
|
||||
```bash
|
||||
# ✗ VERY BAD: Using = instead of ==
|
||||
if [ "$var" = "value" ]; then # Assignment in POSIX test!
|
||||
echo "Match"
|
||||
fi
|
||||
|
||||
# ✓ GOOD: Use == or = correctly
|
||||
if [[ "$var" == "value" ]]; then # Comparison in bash
|
||||
echo "Match"
|
||||
fi
|
||||
|
||||
# ✓ GOOD: POSIX-compliant
|
||||
if [ "$var" = "value" ]; then # Single = is correct in [ ]
|
||||
echo "Match"
|
||||
fi
|
||||
```
|
||||
|
||||
**Why:** In `[[ ]]`, both `=` and `==` work. In `[ ]`, only `=` is POSIX-compliant.
|
||||
|
||||
### Anti-Pattern: Unset Variable Access
|
||||
|
||||
```bash
|
||||
# ✗ BAD: Accessing undefined variables
|
||||
echo "Value: $undefined_variable" # Silent error, prints "Value: "
|
||||
|
||||
# ✓ GOOD: Use set -u
|
||||
set -u
|
||||
echo "Value: $undefined_variable" # Error: undefined_variable: unbound variable
|
||||
|
||||
# ✓ GOOD: Provide default
|
||||
echo "Value: ${undefined_variable:-default}"
|
||||
```
|
||||
|
||||
**Why:** `set -u` catches typos and logic errors early.
|
||||
|
||||
---
|
||||
|
||||
## Command Execution
|
||||
|
||||
### Pattern: Check Command Existence
|
||||
|
||||
```bash
|
||||
# ✓ GOOD: Use command -v
|
||||
if command -v jq &> /dev/null; then
|
||||
echo "jq is installed"
|
||||
else
|
||||
echo "jq is not installed" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# ✗ BAD: Using which
|
||||
if which jq; then # Deprecated, not POSIX
|
||||
echo "jq is installed"
|
||||
fi
|
||||
|
||||
# ✗ BAD: Using type
|
||||
if type jq; then # Verbose output
|
||||
echo "jq is installed"
|
||||
fi
|
||||
```
|
||||
|
||||
**Why:** `command -v` is POSIX-compliant, silent, and reliable.
|
||||
|
||||
### Pattern: Command Substitution
|
||||
|
||||
```bash
|
||||
# ✓ GOOD: Modern syntax with $()
|
||||
result=$(command arg1 arg2)
|
||||
timestamp=$(date +%s)
|
||||
|
||||
# ✗ BAD: Backticks (hard to nest)
|
||||
result=`command arg1 arg2`
|
||||
timestamp=`date +%s`
|
||||
|
||||
# ✓ GOOD: Nested substitution
|
||||
result=$(echo "Outer: $(echo "Inner")")
|
||||
|
||||
# ✗ BAD: Nested backticks (requires escaping)
|
||||
result=`echo "Outer: \`echo \"Inner\"\`"`
|
||||
```
|
||||
|
||||
**Why:** `$()` is easier to read, nest, and maintain.
|
||||
|
||||
### Anti-Pattern: Useless Use of Cat
|
||||
|
||||
```bash
|
||||
# ✗ BAD: UUOC (Useless Use of Cat)
|
||||
cat file.txt | grep "pattern"
|
||||
|
||||
# ✓ GOOD: Direct input
|
||||
grep "pattern" file.txt
|
||||
|
||||
# ✗ BAD: Multiple cats
|
||||
cat file1 | grep pattern | cat | sort | cat
|
||||
|
||||
# ✓ GOOD: Direct pipeline
|
||||
grep pattern file1 | sort
|
||||
```
|
||||
|
||||
**Why:** Unnecessary `cat` wastes resources and adds extra processes.
|
||||
|
||||
### Anti-Pattern: Using ls in Scripts
|
||||
|
||||
```bash
|
||||
# ✗ BAD: Parsing ls output
|
||||
for file in $(ls *.txt); do
|
||||
echo "$file"
|
||||
done
|
||||
|
||||
# ✓ GOOD: Use globbing
|
||||
for file in *.txt; do
|
||||
[[ -f "$file" ]] || continue # Skip if no matches
|
||||
echo "$file"
|
||||
done
|
||||
|
||||
# ✗ BAD: Counting files with ls
|
||||
count=$(ls -1 | wc -l)
|
||||
|
||||
# ✓ GOOD: Use array
|
||||
files=(*)
|
||||
count=${#files[@]}
|
||||
```
|
||||
|
||||
**Why:** `ls` output is meant for humans, not scripts. Parsing it breaks with spaces, newlines, etc.
|
||||
|
||||
---
|
||||
|
||||
## File Operations
|
||||
|
||||
### Pattern: Safe File Reading
|
||||
|
||||
```bash
|
||||
# ✓ GOOD: Preserve leading/trailing whitespace and backslashes
|
||||
while IFS= read -r line; do
|
||||
echo "Line: $line"
|
||||
done < file.txt
|
||||
|
||||
# ✗ BAD: Without IFS= (strips leading/trailing whitespace)
|
||||
while read -r line; do
|
||||
echo "Line: $line"
|
||||
done < file.txt
|
||||
|
||||
# ✗ BAD: Without -r (interprets backslashes)
|
||||
while IFS= read line; do
|
||||
echo "Line: $line"
|
||||
done < file.txt
|
||||
```
|
||||
|
||||
**Why:** `IFS=` prevents trimming, `-r` prevents backslash interpretation.
|
||||
|
||||
### Pattern: Null-Delimited Files
|
||||
|
||||
```bash
|
||||
# ✓ GOOD: For filenames with special characters
|
||||
find . -name "*.txt" -print0 | while IFS= read -r -d '' file; do
|
||||
echo "Processing: $file"
|
||||
done
|
||||
|
||||
# Or with mapfile (bash 4+)
|
||||
mapfile -d '' -t files < <(find . -name "*.txt" -print0)
|
||||
for file in "${files[@]}"; do
|
||||
echo "Processing: $file"
|
||||
done
|
||||
|
||||
# ✗ BAD: Newline-delimited (breaks with newlines in filenames)
|
||||
find . -name "*.txt" | while IFS= read -r file; do
|
||||
echo "Processing: $file"
|
||||
done
|
||||
```
|
||||
|
||||
**Why:** Filenames can contain any character except null and slash.
|
||||
|
||||
### Anti-Pattern: Testing File Existence Incorrectly
|
||||
|
||||
```bash
|
||||
# ✗ BAD: Using ls to test existence
|
||||
if ls file.txt &> /dev/null; then
|
||||
echo "File exists"
|
||||
fi
|
||||
|
||||
# ✓ GOOD: Use test operators
|
||||
if [[ -f file.txt ]]; then
|
||||
echo "File exists"
|
||||
fi
|
||||
|
||||
# ✓ GOOD: Different tests
|
||||
[[ -e path ]] # Exists (file or directory)
|
||||
[[ -f file ]] # Regular file
|
||||
[[ -d dir ]] # Directory
|
||||
[[ -L link ]] # Symbolic link
|
||||
[[ -r file ]] # Readable
|
||||
[[ -w file ]] # Writable
|
||||
[[ -x file ]] # Executable
|
||||
```
|
||||
|
||||
**Why:** Test operators are the correct, efficient way to check file properties.
|
||||
|
||||
### Pattern: Temporary Files
|
||||
|
||||
```bash
|
||||
# ✓ GOOD: Secure temporary file
|
||||
temp_file=$(mktemp)
|
||||
trap 'rm -f "$temp_file"' EXIT
|
||||
|
||||
# Use temp file
|
||||
echo "data" > "$temp_file"
|
||||
|
||||
# ✗ BAD: Insecure temp file
|
||||
temp_file="/tmp/myapp.$$"
|
||||
echo "data" > "$temp_file"
|
||||
# No cleanup!
|
||||
|
||||
# ✓ GOOD: Temporary directory
|
||||
temp_dir=$(mktemp -d)
|
||||
trap 'rm -rf "$temp_dir"' EXIT
|
||||
```
|
||||
|
||||
**Why:** `mktemp` creates secure, unique files and prevents race conditions.
|
||||
|
||||
---
|
||||
|
||||
## String Processing
|
||||
|
||||
### Pattern: String Manipulation with Parameter Expansion
|
||||
|
||||
```bash
|
||||
# ✓ GOOD: Use bash parameter expansion
|
||||
filename="document.tar.gz"
|
||||
basename="${filename%%.*}" # document
|
||||
extension="${filename##*.}" # gz
|
||||
name="${filename%.gz}" # document.tar
|
||||
|
||||
# ✗ BAD: Using external commands
|
||||
basename=$(echo "$filename" | sed 's/\..*$//')
|
||||
extension=$(echo "$filename" | awk -F. '{print $NF}')
|
||||
```
|
||||
|
||||
**Why:** Parameter expansion is faster and doesn't spawn processes.
|
||||
|
||||
### Pattern: String Comparison
|
||||
|
||||
```bash
|
||||
# ✓ GOOD: Use [[ ]] for strings
|
||||
if [[ "$string1" == "$string2" ]]; then
|
||||
echo "Equal"
|
||||
fi
|
||||
|
||||
# ✓ GOOD: Pattern matching
|
||||
if [[ "$filename" == *.txt ]]; then
|
||||
echo "Text file"
|
||||
fi
|
||||
|
||||
# ✓ GOOD: Regex matching
|
||||
if [[ "$email" =~ ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]]; then
|
||||
echo "Valid email"
|
||||
fi
|
||||
|
||||
# ✗ BAD: Using grep for simple string check
|
||||
if echo "$string" | grep -q "substring"; then
|
||||
echo "Found"
|
||||
fi
|
||||
|
||||
# ✓ GOOD: Use substring matching
|
||||
if [[ "$string" == *"substring"* ]]; then
|
||||
echo "Found"
|
||||
fi
|
||||
```
|
||||
|
||||
**Why:** `[[ ]]` is bash-native, faster, and more readable.
|
||||
|
||||
### Anti-Pattern: Word Splitting Issues
|
||||
|
||||
```bash
|
||||
# ✗ BAD: Unquoted expansion with spaces
|
||||
var="file1.txt file2.txt"
|
||||
for file in $var; do # Splits on spaces!
|
||||
echo "$file" # file1.txt, then file2.txt
|
||||
done
|
||||
|
||||
# ✓ GOOD: Use array
|
||||
files=("file1.txt" "file2.txt")
|
||||
for file in "${files[@]}"; do
|
||||
echo "$file"
|
||||
done
|
||||
|
||||
# ✗ BAD: Word splitting in command arguments
|
||||
file="my file.txt"
|
||||
rm $file # Tries to remove "my" and "file.txt"!
|
||||
|
||||
# ✓ GOOD: Quote variables
|
||||
rm "$file"
|
||||
```
|
||||
|
||||
**Why:** Word splitting on spaces is a major source of bugs.
|
||||
|
||||
---
|
||||
|
||||
## Arrays and Loops
|
||||
|
||||
### Pattern: Array Declaration and Use
|
||||
|
||||
```bash
|
||||
# ✓ GOOD: Array declaration
|
||||
files=("file1.txt" "file2.txt" "file 3.txt")
|
||||
|
||||
# ✓ GOOD: Array expansion (each element quoted)
|
||||
for file in "${files[@]}"; do
|
||||
echo "$file"
|
||||
done
|
||||
|
||||
# ✗ BAD: Unquoted array expansion
|
||||
for file in ${files[@]}; do # Word splitting!
|
||||
echo "$file"
|
||||
done
|
||||
|
||||
# ✓ GOOD: Add to array
|
||||
files+=("file4.txt")
|
||||
|
||||
# ✓ GOOD: Array length
|
||||
echo "Count: ${#files[@]}"
|
||||
|
||||
# ✓ GOOD: Array indices
|
||||
for i in "${!files[@]}"; do
|
||||
echo "File $i: ${files[$i]}"
|
||||
done
|
||||
```
|
||||
|
||||
**Why:** Proper array handling prevents word splitting and globbing issues.
|
||||
|
||||
### Pattern: Reading Command Output into Array
|
||||
|
||||
```bash
|
||||
# ✓ GOOD: mapfile/readarray (bash 4+)
|
||||
mapfile -t lines < file.txt
|
||||
|
||||
# ✓ GOOD: With command substitution
|
||||
mapfile -t files < <(find . -name "*.txt")
|
||||
|
||||
# ✗ BAD: Word splitting
|
||||
files=($(find . -name "*.txt")) # Breaks with spaces in filenames!
|
||||
|
||||
# ✓ GOOD: Alternative (POSIX-compatible)
|
||||
while IFS= read -r file; do
|
||||
files+=("$file")
|
||||
done < <(find . -name "*.txt")
|
||||
```
|
||||
|
||||
**Why:** `mapfile` is efficient and handles special characters correctly.
|
||||
|
||||
### Anti-Pattern: C-Style For Loops for Arrays
|
||||
|
||||
```bash
|
||||
# ✗ BAD: C-style loop for arrays
|
||||
for ((i=0; i<${#files[@]}; i++)); do
|
||||
echo "${files[$i]}"
|
||||
done
|
||||
|
||||
# ✓ GOOD: For-in loop
|
||||
for file in "${files[@]}"; do
|
||||
echo "$file"
|
||||
done
|
||||
|
||||
# ✓ ACCEPTABLE: When you need the index
|
||||
for i in "${!files[@]}"; do
|
||||
echo "Index $i: ${files[$i]}"
|
||||
done
|
||||
```
|
||||
|
||||
**Why:** For-in loops are simpler and less error-prone.
|
||||
|
||||
### Pattern: Loop over Range
|
||||
|
||||
```bash
|
||||
# ✓ GOOD: Brace expansion
|
||||
for i in {1..10}; do
|
||||
echo "$i"
|
||||
done
|
||||
|
||||
# ✓ GOOD: With variables (bash 4+)
|
||||
start=1
|
||||
end=10
|
||||
for i in $(seq $start $end); do
|
||||
echo "$i"
|
||||
done
|
||||
|
||||
# ✓ GOOD: C-style (arithmetic)
|
||||
for ((i=1; i<=10; i++)); do
|
||||
echo "$i"
|
||||
done
|
||||
|
||||
# ✗ BAD: Using seq in a loop unnecessarily
|
||||
for i in $(seq 1 1000000); do # Creates huge string in memory!
|
||||
echo "$i"
|
||||
done
|
||||
|
||||
# ✓ GOOD: Use C-style for large ranges
|
||||
for ((i=1; i<=1000000; i++)); do
|
||||
echo "$i"
|
||||
done
|
||||
```
|
||||
|
||||
**Why:** Choose the right loop construct based on the use case.
|
||||
|
||||
---
|
||||
|
||||
## Conditionals and Tests
|
||||
|
||||
### Pattern: File Tests
|
||||
|
||||
```bash
|
||||
# ✓ GOOD: Use appropriate test
|
||||
if [[ -f "$file" ]]; then # Regular file
|
||||
if [[ -d "$dir" ]]; then # Directory
|
||||
if [[ -e "$path" ]]; then # Exists (any type)
|
||||
if [[ -L "$link" ]]; then # Symbolic link
|
||||
if [[ -r "$file" ]]; then # Readable
|
||||
if [[ -w "$file" ]]; then # Writable
|
||||
if [[ -x "$file" ]]; then # Executable
|
||||
if [[ -s "$file" ]]; then # Non-empty file
|
||||
|
||||
# ✗ BAD: Incorrect test
|
||||
if [[ -e "$file" ]]; then # Exists, but could be directory!
|
||||
cat "$file" # Fails if directory
|
||||
fi
|
||||
|
||||
# ✓ GOOD: Specific test
|
||||
if [[ -f "$file" ]]; then
|
||||
cat "$file"
|
||||
fi
|
||||
```
|
||||
|
||||
**Why:** Use the most specific test for your use case.
|
||||
|
||||
### Pattern: Numeric Comparison
|
||||
|
||||
```bash
|
||||
# ✓ GOOD: Arithmetic context
|
||||
if (( num > 10 )); then
|
||||
echo "Greater than 10"
|
||||
fi
|
||||
|
||||
# ✓ GOOD: Test operator
|
||||
if [[ $num -gt 10 ]]; then
|
||||
echo "Greater than 10"
|
||||
fi
|
||||
|
||||
# ✗ BAD: String comparison for numbers
|
||||
if [[ "$num" > "10" ]]; then # Lexicographic comparison!
|
||||
echo "Greater than 10" # "9" > "10" is true!
|
||||
fi
|
||||
```
|
||||
|
||||
**Why:** Use numeric comparison operators for numbers.
|
||||
|
||||
### Anti-Pattern: Testing Boolean Strings
|
||||
|
||||
```bash
|
||||
# ✗ BAD: Comparing to string "true"
|
||||
if [[ "$flag" == "true" ]]; then
|
||||
do_something
|
||||
fi
|
||||
|
||||
# ✓ GOOD: Use boolean variable directly
|
||||
flag=false # or true
|
||||
|
||||
if $flag; then
|
||||
do_something
|
||||
fi
|
||||
|
||||
# ✓ BETTER: Use integers for flags
|
||||
flag=0 # false
|
||||
flag=1 # true
|
||||
|
||||
if (( flag )); then
|
||||
do_something
|
||||
fi
|
||||
|
||||
# ✓ GOOD: For command success/failure
|
||||
if command; then
|
||||
echo "Success"
|
||||
fi
|
||||
```
|
||||
|
||||
**Why:** Boolean strings are error-prone; use actual booleans or return codes.
|
||||
|
||||
### Pattern: Multiple Conditions
|
||||
|
||||
```bash
|
||||
# ✓ GOOD: Logical operators
|
||||
if [[ condition1 && condition2 ]]; then
|
||||
echo "Both true"
|
||||
fi
|
||||
|
||||
if [[ condition1 || condition2 ]]; then
|
||||
echo "At least one true"
|
||||
fi
|
||||
|
||||
if [[ ! condition ]]; then
|
||||
echo "False"
|
||||
fi
|
||||
|
||||
# ✗ BAD: Separate tests
|
||||
if [ condition1 -a condition2 ]; then # Deprecated
|
||||
echo "Both true"
|
||||
fi
|
||||
|
||||
# ✗ BAD: Nested ifs for AND
|
||||
if [[ condition1 ]]; then
|
||||
if [[ condition2 ]]; then
|
||||
echo "Both true"
|
||||
fi
|
||||
fi
|
||||
```
|
||||
|
||||
**Why:** `&&` and `||` in `[[ ]]` are clearer and recommended.
|
||||
|
||||
---
|
||||
|
||||
## Functions
|
||||
|
||||
### Pattern: Function Return Values
|
||||
|
||||
```bash
|
||||
# ✓ GOOD: Return status, output to stdout
|
||||
get_value() {
|
||||
local value="result"
|
||||
|
||||
if [[ -n "$value" ]]; then
|
||||
echo "$value"
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Usage
|
||||
if result=$(get_value); then
|
||||
echo "Got: $result"
|
||||
else
|
||||
echo "Failed"
|
||||
fi
|
||||
|
||||
# ✗ BAD: Using return for data
|
||||
get_value() {
|
||||
return 42 # Can only return 0-255!
|
||||
}
|
||||
result=$? # Gets 42, but limited range
|
||||
```
|
||||
|
||||
**Why:** `return` is for exit status (0-255), not data. Output to stdout for data.
|
||||
|
||||
### Pattern: Local Variables in Functions
|
||||
|
||||
```bash
|
||||
# ✓ GOOD: Declare local variables
|
||||
my_function() {
|
||||
local arg="$1"
|
||||
local result=""
|
||||
|
||||
result=$(process "$arg")
|
||||
echo "$result"
|
||||
}
|
||||
|
||||
# ✗ BAD: Global variables
|
||||
my_function() {
|
||||
arg="$1" # Pollutes global namespace!
|
||||
result="" # Global variable!
|
||||
|
||||
result=$(process "$arg")
|
||||
echo "$result"
|
||||
}
|
||||
```
|
||||
|
||||
**Why:** Local variables prevent unexpected side effects.
|
||||
|
||||
### Anti-Pattern: Capturing Local Command Failure
|
||||
|
||||
```bash
|
||||
# ✗ BAD: Local declaration masks command failure
|
||||
my_function() {
|
||||
local result=$(command_that_fails) # $? is from 'local', not 'command'!
|
||||
echo "$result"
|
||||
}
|
||||
|
||||
# ✓ GOOD: Separate declaration and assignment
|
||||
my_function() {
|
||||
local result
|
||||
result=$(command_that_fails) || return 1
|
||||
echo "$result"
|
||||
}
|
||||
|
||||
# ✓ GOOD: Check command separately
|
||||
my_function() {
|
||||
local result
|
||||
|
||||
if ! result=$(command_that_fails); then
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo "$result"
|
||||
}
|
||||
```
|
||||
|
||||
**Why:** Combining `local` and command substitution hides command failure.
|
||||
|
||||
---
|
||||
|
||||
## Error Handling
|
||||
|
||||
### Pattern: Check Command Success
|
||||
|
||||
```bash
|
||||
# ✓ GOOD: Direct check
|
||||
if ! command; then
|
||||
echo "Command failed" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# ✓ GOOD: With logical operator
|
||||
command || {
|
||||
echo "Command failed" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
# ✓ GOOD: Capture output and check
|
||||
if ! output=$(command 2>&1); then
|
||||
echo "Command failed: $output" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# ✗ BAD: Not checking status
|
||||
command # What if it fails?
|
||||
next_command
|
||||
```
|
||||
|
||||
**Why:** Always check if commands succeed unless failure is acceptable.
|
||||
|
||||
### Pattern: Error Messages to stderr
|
||||
|
||||
```bash
|
||||
# ✓ GOOD: Errors to stderr
|
||||
echo "Error: Invalid argument" >&2
|
||||
|
||||
# ✗ BAD: Errors to stdout
|
||||
echo "Error: Invalid argument"
|
||||
|
||||
# ✓ GOOD: Error function
|
||||
error() {
|
||||
echo "ERROR: $*" >&2
|
||||
}
|
||||
|
||||
error "Something went wrong"
|
||||
```
|
||||
|
||||
**Why:** stderr is for errors, stdout is for data output.
|
||||
|
||||
### Pattern: Cleanup on Exit
|
||||
|
||||
```bash
|
||||
# ✓ GOOD: Trap for cleanup
|
||||
temp_file=$(mktemp)
|
||||
|
||||
cleanup() {
|
||||
rm -f "$temp_file"
|
||||
}
|
||||
|
||||
trap cleanup EXIT
|
||||
|
||||
# Do work with temp_file
|
||||
|
||||
# ✗ BAD: Manual cleanup (might not run)
|
||||
temp_file=$(mktemp)
|
||||
|
||||
# Do work
|
||||
|
||||
rm -f "$temp_file" # Doesn't run if script exits early!
|
||||
```
|
||||
|
||||
**Why:** Trap ensures cleanup runs on exit, even on errors.
|
||||
|
||||
### Anti-Pattern: Silencing Errors
|
||||
|
||||
```bash
|
||||
# ✗ BAD: Silencing errors
|
||||
command 2>/dev/null # What if it fails?
|
||||
next_command
|
||||
|
||||
# ✓ GOOD: Check status even if silencing output
|
||||
if ! command 2>/dev/null; then
|
||||
echo "Command failed" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# ✓ ACCEPTABLE: When failure is expected and acceptable
|
||||
if command 2>/dev/null; then
|
||||
echo "Command succeeded"
|
||||
else
|
||||
echo "Command failed (expected)"
|
||||
fi
|
||||
```
|
||||
|
||||
**Why:** Silencing errors without checking status leads to silent failures.
|
||||
|
||||
---
|
||||
|
||||
## Process Management
|
||||
|
||||
### Pattern: Background Jobs
|
||||
|
||||
```bash
|
||||
# ✓ GOOD: Track background jobs
|
||||
long_running_task &
|
||||
pid=$!
|
||||
|
||||
# Wait for completion
|
||||
if wait "$pid"; then
|
||||
echo "Task completed successfully"
|
||||
else
|
||||
echo "Task failed" >&2
|
||||
fi
|
||||
|
||||
# ✓ GOOD: Multiple background jobs
|
||||
job1 &
|
||||
pid1=$!
|
||||
job2 &
|
||||
pid2=$!
|
||||
|
||||
wait "$pid1" "$pid2"
|
||||
```
|
||||
|
||||
**Why:** Proper job management prevents zombie processes.
|
||||
|
||||
### Pattern: Timeout for Commands
|
||||
|
||||
```bash
|
||||
# ✓ GOOD: Use timeout command (if available)
|
||||
if timeout 30 long_running_command; then
|
||||
echo "Completed within timeout"
|
||||
else
|
||||
echo "Timed out or failed" >&2
|
||||
fi
|
||||
|
||||
# ✓ GOOD: Manual timeout implementation
|
||||
timeout_command() {
|
||||
local timeout=$1
|
||||
shift
|
||||
|
||||
"$@" &
|
||||
local pid=$!
|
||||
|
||||
( sleep "$timeout"; kill "$pid" 2>/dev/null ) &
|
||||
local killer=$!
|
||||
|
||||
if wait "$pid" 2>/dev/null; then
|
||||
kill "$killer" 2>/dev/null
|
||||
wait "$killer" 2>/dev/null
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
timeout_command 30 long_running_command
|
||||
```
|
||||
|
||||
**Why:** Prevents scripts from hanging indefinitely.
|
||||
|
||||
### Anti-Pattern: Killing Processes Unsafely
|
||||
|
||||
```bash
|
||||
# ✗ BAD: kill -9 immediately
|
||||
kill -9 "$pid"
|
||||
|
||||
# ✓ GOOD: Graceful shutdown first
|
||||
kill -TERM "$pid"
|
||||
sleep 2
|
||||
|
||||
if kill -0 "$pid" 2>/dev/null; then
|
||||
echo "Process still running, forcing..." >&2
|
||||
kill -KILL "$pid"
|
||||
fi
|
||||
|
||||
# ✓ GOOD: With timeout
|
||||
graceful_kill() {
|
||||
local pid=$1
|
||||
local timeout=${2:-10}
|
||||
|
||||
kill -TERM "$pid" 2>/dev/null || return 0
|
||||
|
||||
for ((i=0; i<timeout; i++)); do
|
||||
if ! kill -0 "$pid" 2>/dev/null; then
|
||||
return 0
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
|
||||
echo "Forcing kill of $pid" >&2
|
||||
kill -KILL "$pid" 2>/dev/null
|
||||
}
|
||||
```
|
||||
|
||||
**Why:** SIGTERM allows graceful shutdown; SIGKILL should be last resort.
|
||||
|
||||
---
|
||||
|
||||
## Security Patterns
|
||||
|
||||
### Pattern: Input Validation
|
||||
|
||||
```bash
|
||||
# ✓ GOOD: Whitelist validation
|
||||
validate_action() {
|
||||
local action=$1
|
||||
case "$action" in
|
||||
start|stop|restart|status)
|
||||
return 0
|
||||
;;
|
||||
*)
|
||||
echo "Error: Invalid action: $action" >&2
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# ✗ BAD: No validation
|
||||
action="$1"
|
||||
systemctl "$action" myservice # User can pass arbitrary commands!
|
||||
|
||||
# ✓ GOOD: Validate first
|
||||
if validate_action "$1"; then
|
||||
systemctl "$1" myservice
|
||||
else
|
||||
exit 1
|
||||
fi
|
||||
```
|
||||
|
||||
**Why:** Whitelist validation prevents command injection.
|
||||
|
||||
### Pattern: Avoid eval
|
||||
|
||||
```bash
|
||||
# ✗ BAD: eval with user input
|
||||
eval "$user_command" # DANGEROUS!
|
||||
|
||||
# ✓ GOOD: Use arrays
|
||||
command_args=("$arg1" "$arg2" "$arg3")
|
||||
command "${command_args[@]}"
|
||||
|
||||
# ✗ BAD: Dynamic variable names
|
||||
eval "var_$name=value"
|
||||
|
||||
# ✓ GOOD: Associative arrays (bash 4+)
|
||||
declare -A vars
|
||||
vars[$name]="value"
|
||||
```
|
||||
|
||||
**Why:** `eval` with user input is a security vulnerability.
|
||||
|
||||
### Pattern: Safe PATH
|
||||
|
||||
```bash
|
||||
# ✓ GOOD: Set explicit PATH
|
||||
export PATH="/usr/local/bin:/usr/bin:/bin"
|
||||
|
||||
# ✓ GOOD: Use absolute paths for critical commands
|
||||
/usr/bin/rm -rf "$directory"
|
||||
|
||||
# ✗ BAD: Trusting user's PATH
|
||||
rm -rf "$directory" # What if there's a malicious 'rm' in PATH?
|
||||
```
|
||||
|
||||
**Why:** Prevents PATH injection attacks.
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
**Most Critical Patterns:**
|
||||
|
||||
1. Always quote variable expansions: `"$var"`
|
||||
2. Use `set -euo pipefail` for safety
|
||||
3. Prefer `[[ ]]` over `[ ]` in bash
|
||||
4. Use arrays for lists: `"${array[@]}"`
|
||||
5. Check command success: `if ! command; then`
|
||||
6. Use local variables in functions
|
||||
7. Errors to stderr: `echo "Error" >&2`
|
||||
8. Use `mktemp` for temporary files
|
||||
9. Cleanup with traps: `trap cleanup EXIT`
|
||||
10. Validate all user input
|
||||
|
||||
**Most Dangerous Anti-Patterns:**
|
||||
|
||||
1. Unquoted variables: `$var`
|
||||
2. Parsing `ls` output
|
||||
3. Using `eval` with user input
|
||||
4. Silencing errors without checking
|
||||
5. Not using `set -u` or defaults
|
||||
6. Global variables in functions
|
||||
7. Word splitting on filenames
|
||||
8. Testing strings with `>` for numbers
|
||||
9. `kill -9` without trying graceful shutdown
|
||||
10. Trusting user PATH
|
||||
|
||||
Following these patterns and avoiding anti-patterns will result in robust, secure, and maintainable bash scripts.
|
||||
1080
skills/bash-master/references/platform_specifics.md
Normal file
1080
skills/bash-master/references/platform_specifics.md
Normal file
File diff suppressed because it is too large
Load Diff
1041
skills/bash-master/references/platform_specifics.md.backup
Normal file
1041
skills/bash-master/references/platform_specifics.md.backup
Normal file
File diff suppressed because it is too large
Load Diff
739
skills/bash-master/references/resources.md
Normal file
739
skills/bash-master/references/resources.md
Normal file
@@ -0,0 +1,739 @@
|
||||
# Bash Scripting Resources
|
||||
|
||||
Comprehensive directory of authoritative sources, tools, and learning resources for bash scripting.
|
||||
|
||||
---
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [Official Documentation](#official-documentation)
|
||||
2. [Style Guides and Standards](#style-guides-and-standards)
|
||||
3. [Tools and Utilities](#tools-and-utilities)
|
||||
4. [Learning Resources](#learning-resources)
|
||||
5. [Community Resources](#community-resources)
|
||||
6. [Books](#books)
|
||||
7. [Cheat Sheets and Quick References](#cheat-sheets-and-quick-references)
|
||||
8. [Testing and Quality](#testing-and-quality)
|
||||
9. [Platform-Specific Resources](#platform-specific-resources)
|
||||
10. [Advanced Topics](#advanced-topics)
|
||||
|
||||
---
|
||||
|
||||
## 🚨 CRITICAL GUIDELINES
|
||||
|
||||
### Windows File Path Requirements
|
||||
|
||||
**MANDATORY: Always Use Backslashes on Windows for File Paths**
|
||||
|
||||
When using Edit or Write tools on Windows, you MUST use backslashes (`\`) in file paths, NOT forward slashes (`/`).
|
||||
|
||||
**Examples:**
|
||||
- ❌ WRONG: `D:/repos/project/file.tsx`
|
||||
- ✅ CORRECT: `D:\repos\project\file.tsx`
|
||||
|
||||
This applies to:
|
||||
- Edit tool file_path parameter
|
||||
- Write tool file_path parameter
|
||||
- All file operations on Windows systems
|
||||
|
||||
|
||||
### Documentation Guidelines
|
||||
|
||||
**NEVER create new documentation files unless explicitly requested by the user.**
|
||||
|
||||
- **Priority**: Update existing README.md files rather than creating new documentation
|
||||
- **Repository cleanliness**: Keep repository root clean - only README.md unless user requests otherwise
|
||||
- **Style**: Documentation should be concise, direct, and professional - avoid AI-generated tone
|
||||
- **User preference**: Only create additional .md files when user specifically asks for documentation
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Official Documentation
|
||||
|
||||
### Bash Manual
|
||||
|
||||
**GNU Bash Reference Manual**
|
||||
- **URL:** https://www.gnu.org/software/bash/manual/
|
||||
- **Description:** The authoritative reference for bash features, syntax, and built-ins
|
||||
- **Use for:** Detailed feature documentation, syntax clarification, version-specific features
|
||||
|
||||
**Bash Man Page**
|
||||
```bash
|
||||
man bash # Complete bash documentation
|
||||
man bash-builtins # Built-in commands
|
||||
```
|
||||
- **Use for:** Quick reference on local system, offline documentation
|
||||
|
||||
### POSIX Standards
|
||||
|
||||
**POSIX Shell Command Language**
|
||||
- **URL:** https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html
|
||||
- **Description:** IEEE/Open Group specification for portable shell scripting
|
||||
- **Use for:** Writing portable scripts, understanding sh vs bash differences
|
||||
|
||||
**POSIX Utilities**
|
||||
- **URL:** https://pubs.opengroup.org/onlinepubs/9699919799/idx/utilities.html
|
||||
- **Description:** Standard utilities available in POSIX-compliant systems
|
||||
- **Use for:** Portable command usage, cross-platform compatibility
|
||||
|
||||
### Command Documentation
|
||||
|
||||
**GNU Coreutils Manual**
|
||||
- **URL:** https://www.gnu.org/software/coreutils/manual/
|
||||
- **Description:** Documentation for core GNU utilities (ls, cat, grep, etc.)
|
||||
- **Use for:** Understanding Linux command behavior, GNU-specific features
|
||||
|
||||
**Man Pages Online**
|
||||
- **URL:** https://man7.org/linux/man-pages/
|
||||
- **URL:** https://www.freebsd.org/cgi/man.cgi (BSD/macOS)
|
||||
- **Description:** Online searchable man pages
|
||||
- **Use for:** Quick online reference, comparing Linux vs BSD commands
|
||||
|
||||
---
|
||||
|
||||
## Style Guides and Standards
|
||||
|
||||
### Google Shell Style Guide
|
||||
|
||||
**URL:** https://google.github.io/styleguide/shellguide.html
|
||||
|
||||
**Key Points:**
|
||||
- Industry-standard practices from Google
|
||||
- Covers naming conventions, formatting, best practices
|
||||
- When to use shell vs other languages
|
||||
- Safety and portability guidelines
|
||||
|
||||
**Use for:** Professional code style, team standards, code reviews
|
||||
|
||||
### Defensive Bash Programming
|
||||
|
||||
**URL:** https://kfirlavi.herokuapp.com/blog/2012/11/14/defensive-bash-programming
|
||||
|
||||
**Key Points:**
|
||||
- Writing robust bash scripts
|
||||
- Error handling patterns
|
||||
- Safe coding practices
|
||||
- Code organization
|
||||
|
||||
**Use for:** Improving script reliability, avoiding common pitfalls
|
||||
|
||||
### Shell Style Guide (GitHub)
|
||||
|
||||
**URL:** https://github.com/bahamas10/bash-style-guide
|
||||
|
||||
**Key Points:**
|
||||
- Community-driven style guidelines
|
||||
- Practical examples
|
||||
- Modern bash features
|
||||
|
||||
**Use for:** Alternative perspectives on style, community standards
|
||||
|
||||
---
|
||||
|
||||
## Tools and Utilities
|
||||
|
||||
### ShellCheck
|
||||
|
||||
**Website:** https://www.shellcheck.net/
|
||||
**GitHub:** https://github.com/koalaman/shellcheck
|
||||
**Online Tool:** https://www.shellcheck.net/ (paste code for instant feedback)
|
||||
|
||||
**Description:** Static analysis tool for shell scripts
|
||||
|
||||
**Installation:**
|
||||
```bash
|
||||
# Ubuntu/Debian
|
||||
apt-get install shellcheck
|
||||
|
||||
# macOS
|
||||
brew install shellcheck
|
||||
|
||||
# Windows (Scoop)
|
||||
scoop install shellcheck
|
||||
|
||||
# Via Docker
|
||||
docker run --rm -v "$PWD:/mnt" koalaman/shellcheck script.sh
|
||||
```
|
||||
|
||||
**Usage:**
|
||||
```bash
|
||||
shellcheck script.sh # Check script
|
||||
shellcheck -x script.sh # Follow source statements
|
||||
shellcheck -f json script.sh # JSON output
|
||||
shellcheck -e SC2086 script.sh # Exclude specific warnings
|
||||
```
|
||||
|
||||
**ShellCheck Wiki:** https://www.shellcheck.net/wiki/
|
||||
- Detailed explanations of every warning
|
||||
- **Use for:** Understanding and fixing ShellCheck warnings
|
||||
|
||||
### shfmt
|
||||
|
||||
**GitHub:** https://github.com/mvdan/sh
|
||||
|
||||
**Description:** Shell script formatter
|
||||
|
||||
**Installation:**
|
||||
```bash
|
||||
# macOS
|
||||
brew install shfmt
|
||||
|
||||
# Go
|
||||
go install mvdan.cc/sh/v3/cmd/shfmt@latest
|
||||
```
|
||||
|
||||
**Usage:**
|
||||
```bash
|
||||
shfmt -i 4 -w script.sh # Format with 4-space indent
|
||||
shfmt -d script.sh # Show diff without modifying
|
||||
shfmt -l script.sh # List files that would be changed
|
||||
```
|
||||
|
||||
**Use for:** Consistent code formatting, automated formatting in CI
|
||||
|
||||
### BATS (Bash Automated Testing System)
|
||||
|
||||
**GitHub:** https://github.com/bats-core/bats-core
|
||||
|
||||
**Description:** Testing framework for bash scripts
|
||||
|
||||
**Installation:**
|
||||
```bash
|
||||
git clone https://github.com/bats-core/bats-core.git
|
||||
cd bats-core
|
||||
./install.sh /usr/local
|
||||
```
|
||||
|
||||
**Usage:**
|
||||
```bash
|
||||
bats test/ # Run all tests
|
||||
bats test/script.bats # Run specific test file
|
||||
bats --tap test/ # TAP output format
|
||||
```
|
||||
|
||||
**Documentation:** https://bats-core.readthedocs.io/
|
||||
|
||||
**Use for:** Unit testing bash scripts, CI/CD integration
|
||||
|
||||
### bashate
|
||||
|
||||
**GitHub:** https://github.com/openstack/bashate
|
||||
|
||||
**Description:** Style checker (used by OpenStack)
|
||||
|
||||
**Installation:**
|
||||
```bash
|
||||
pip install bashate
|
||||
```
|
||||
|
||||
**Usage:**
|
||||
```bash
|
||||
bashate script.sh
|
||||
bashate -i E006 script.sh # Ignore specific errors
|
||||
```
|
||||
|
||||
**Use for:** Additional style checking beyond ShellCheck
|
||||
|
||||
### checkbashisms
|
||||
|
||||
**Package:** devscripts (Debian)
|
||||
|
||||
**Description:** Checks for bashisms in sh scripts
|
||||
|
||||
**Installation:**
|
||||
```bash
|
||||
apt-get install devscripts # Ubuntu/Debian
|
||||
```
|
||||
|
||||
**Usage:**
|
||||
```bash
|
||||
checkbashisms script.sh
|
||||
checkbashisms -f script.sh # Force check even if #!/bin/bash
|
||||
```
|
||||
|
||||
**Use for:** Ensuring POSIX compliance, portable scripts
|
||||
|
||||
---
|
||||
|
||||
## Learning Resources
|
||||
|
||||
### Interactive Tutorials
|
||||
|
||||
**Bash Academy**
|
||||
- **URL:** https://www.bash.academy/
|
||||
- **Description:** Modern, comprehensive bash tutorial
|
||||
- **Topics:** Basics, scripting, advanced features
|
||||
- **Use for:** Learning bash from scratch, structured learning path
|
||||
|
||||
**Learn Shell**
|
||||
- **URL:** https://www.learnshell.org/
|
||||
- **Description:** Interactive bash tutorial with exercises
|
||||
- **Use for:** Hands-on practice, beginners
|
||||
|
||||
**Bash Scripting Tutorial**
|
||||
- **URL:** https://linuxconfig.org/bash-scripting-tutorial
|
||||
- **Description:** Comprehensive tutorial series
|
||||
- **Use for:** Step-by-step learning, examples
|
||||
|
||||
### Guides and Documentation
|
||||
|
||||
**Bash Guide for Beginners**
|
||||
- **URL:** https://tldp.org/LDP/Bash-Beginners-Guide/html/
|
||||
- **Author:** The Linux Documentation Project
|
||||
- **Description:** Comprehensive guide covering basics to intermediate
|
||||
- **Use for:** Structured learning, reference material
|
||||
|
||||
**Advanced Bash-Scripting Guide**
|
||||
- **URL:** https://tldp.org/LDP/abs/html/
|
||||
- **Description:** In-depth coverage of advanced bash topics
|
||||
- **Topics:** Complex scripting, text processing, system administration
|
||||
- **Use for:** Advanced techniques, real-world examples
|
||||
|
||||
**Bash Hackers Wiki**
|
||||
- **URL:** https://wiki.bash-hackers.org/
|
||||
- **Alternative:** https://flokoe.github.io/bash-hackers-wiki/ (maintained mirror)
|
||||
- **Description:** Community-driven bash documentation
|
||||
- **Use for:** In-depth explanations, advanced topics, edge cases
|
||||
|
||||
**Greg's Wiki (Wooledge)**
|
||||
- **URL:** https://mywiki.wooledge.org/
|
||||
- **Key Pages:**
|
||||
- https://mywiki.wooledge.org/BashFAQ
|
||||
- https://mywiki.wooledge.org/BashPitfalls
|
||||
- https://mywiki.wooledge.org/BashGuide
|
||||
- **Description:** High-quality bash Q&A and guides
|
||||
- **Use for:** Common questions, avoiding pitfalls, best practices
|
||||
|
||||
### Video Courses
|
||||
|
||||
**Bash Scripting on Linux (Udemy)**
|
||||
- **Description:** Comprehensive video course
|
||||
- **Use for:** Visual learners
|
||||
|
||||
**Shell Scripting: Discover How to Automate Command Line Tasks (Udemy)**
|
||||
- **Description:** Practical shell scripting course
|
||||
- **Use for:** Automation-focused learning
|
||||
|
||||
**LinkedIn Learning - Learning Bash Scripting**
|
||||
- **Description:** Professional development course
|
||||
- **Use for:** Structured corporate training
|
||||
|
||||
---
|
||||
|
||||
## Community Resources
|
||||
|
||||
### Stack Overflow
|
||||
|
||||
**Bash Tag**
|
||||
- **URL:** https://stackoverflow.com/questions/tagged/bash
|
||||
- **Use for:** Specific problems, code review, troubleshooting
|
||||
|
||||
**Top Questions:**
|
||||
- **URL:** https://stackoverflow.com/questions/tagged/bash?tab=Votes
|
||||
- **Use for:** Common problems and solutions
|
||||
|
||||
### Unix & Linux Stack Exchange
|
||||
|
||||
**URL:** https://unix.stackexchange.com/
|
||||
|
||||
**Shell Tag:** https://unix.stackexchange.com/questions/tagged/shell
|
||||
**Bash Tag:** https://unix.stackexchange.com/questions/tagged/bash
|
||||
|
||||
**Use for:** Unix/Linux-specific questions, system administration
|
||||
|
||||
### Reddit
|
||||
|
||||
**/r/bash**
|
||||
- **URL:** https://www.reddit.com/r/bash/
|
||||
- **Description:** Bash scripting community
|
||||
- **Use for:** Discussions, learning resources, help
|
||||
|
||||
**/r/commandline**
|
||||
- **URL:** https://www.reddit.com/r/commandline/
|
||||
- **Description:** Command-line interface community
|
||||
- **Use for:** CLI tips, tools, productivity
|
||||
|
||||
### IRC/Chat
|
||||
|
||||
**Freenode #bash**
|
||||
- **URL:** irc://irc.freenode.net/bash
|
||||
- **Description:** Real-time bash help channel
|
||||
- **Use for:** Live help, quick questions
|
||||
|
||||
**Libera.Chat #bash**
|
||||
- **URL:** irc://irc.libera.chat/bash
|
||||
- **Description:** Alternative IRC channel
|
||||
- **Use for:** Live community support
|
||||
|
||||
---
|
||||
|
||||
## Books
|
||||
|
||||
### "Classic Shell Scripting" by Arnold Robbins & Nelson Beebe
|
||||
|
||||
**Publisher:** O'Reilly
|
||||
**ISBN:** 978-0596005955
|
||||
|
||||
**Topics:**
|
||||
- Shell basics and portability
|
||||
- Text processing and filters
|
||||
- Shell programming patterns
|
||||
|
||||
**Use for:** Comprehensive reference, professional development
|
||||
|
||||
### "Learning the bash Shell" by Cameron Newham
|
||||
|
||||
**Publisher:** O'Reilly
|
||||
**ISBN:** 978-0596009656
|
||||
|
||||
**Topics:**
|
||||
- Bash basics
|
||||
- Command-line editing
|
||||
- Shell programming
|
||||
|
||||
**Use for:** Systematic learning, reference
|
||||
|
||||
### "Bash Cookbook" by Carl Albing & JP Vossen
|
||||
|
||||
**Publisher:** O'Reilly
|
||||
**ISBN:** 978-1491975336
|
||||
|
||||
**Topics:**
|
||||
- Solutions to common problems
|
||||
- Recipes and patterns
|
||||
- Real-world examples
|
||||
|
||||
**Use for:** Problem-solving, practical examples
|
||||
|
||||
### "Wicked Cool Shell Scripts" by Dave Taylor & Brandon Perry
|
||||
|
||||
**Publisher:** No Starch Press
|
||||
**ISBN:** 978-1593276027
|
||||
|
||||
**Topics:**
|
||||
- Creative shell scripting
|
||||
- System administration
|
||||
- Fun and practical scripts
|
||||
|
||||
**Use for:** Inspiration, practical applications
|
||||
|
||||
### "The Linux Command Line" by William Shotts
|
||||
|
||||
**Publisher:** No Starch Press
|
||||
**ISBN:** 978-1593279523
|
||||
**Free PDF:** https://linuxcommand.org/tlcl.php
|
||||
|
||||
**Topics:**
|
||||
- Command-line basics
|
||||
- Shell scripting fundamentals
|
||||
- Linux system administration
|
||||
|
||||
**Use for:** Beginners, comprehensive introduction
|
||||
|
||||
---
|
||||
|
||||
## Cheat Sheets and Quick References
|
||||
|
||||
### Bash Cheat Sheet (DevHints)
|
||||
|
||||
**URL:** https://devhints.io/bash
|
||||
|
||||
**Content:**
|
||||
- Quick syntax reference
|
||||
- Common patterns
|
||||
- Parameter expansion
|
||||
- Conditionals and loops
|
||||
|
||||
**Use for:** Quick lookups, syntax reminders
|
||||
|
||||
### Bash Scripting Cheat Sheet (GitHub)
|
||||
|
||||
**URL:** https://github.com/LeCoupa/awesome-cheatsheets/blob/master/languages/bash.sh
|
||||
|
||||
**Content:**
|
||||
- Comprehensive syntax guide
|
||||
- Examples and explanations
|
||||
- Best practices
|
||||
|
||||
**Use for:** Single-file reference
|
||||
|
||||
### explainshell.com
|
||||
|
||||
**URL:** https://explainshell.com/
|
||||
|
||||
**Description:** Interactive tool that explains shell commands
|
||||
|
||||
**Example:** Paste `tar -xzvf file.tar.gz` to get detailed explanation of each flag
|
||||
|
||||
**Use for:** Understanding complex commands, learning command options
|
||||
|
||||
### Command Line Fu
|
||||
|
||||
**URL:** https://www.commandlinefu.com/
|
||||
|
||||
**Description:** Community-contributed command-line snippets
|
||||
|
||||
**Use for:** One-liners, clever solutions, learning new commands
|
||||
|
||||
### tldr Pages
|
||||
|
||||
**URL:** https://tldr.sh/
|
||||
**GitHub:** https://github.com/tldr-pages/tldr
|
||||
|
||||
**Description:** Simplified man pages with examples
|
||||
|
||||
**Installation:**
|
||||
```bash
|
||||
npm install -g tldr
|
||||
# Or
|
||||
brew install tldr
|
||||
```
|
||||
|
||||
**Usage:**
|
||||
```bash
|
||||
tldr tar
|
||||
tldr grep
|
||||
tldr find
|
||||
```
|
||||
|
||||
**Use for:** Quick command examples, practical usage
|
||||
|
||||
---
|
||||
|
||||
## Testing and Quality
|
||||
|
||||
### Testing Frameworks
|
||||
|
||||
**BATS (Bash Automated Testing System)**
|
||||
- **URL:** https://github.com/bats-core/bats-core
|
||||
- **Documentation:** https://bats-core.readthedocs.io/
|
||||
- **Use for:** Unit testing
|
||||
|
||||
**shUnit2**
|
||||
- **URL:** https://github.com/kward/shunit2
|
||||
- **Description:** xUnit-based unit testing framework
|
||||
- **Use for:** Alternative to BATS
|
||||
|
||||
**Bash Unit**
|
||||
- **URL:** https://github.com/pgrange/bash_unit
|
||||
- **Description:** Bash unit testing
|
||||
- **Use for:** Lightweight testing
|
||||
|
||||
### CI/CD Integration
|
||||
|
||||
**GitHub Actions Example**
|
||||
```yaml
|
||||
name: Test
|
||||
on: [push, pull_request]
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Install ShellCheck
|
||||
run: sudo apt-get install -y shellcheck
|
||||
- name: Run ShellCheck
|
||||
run: find . -name "*.sh" -exec shellcheck {} +
|
||||
- name: Install BATS
|
||||
run: |
|
||||
git clone https://github.com/bats-core/bats-core.git
|
||||
cd bats-core
|
||||
sudo ./install.sh /usr/local
|
||||
- name: Run Tests
|
||||
run: bats test/
|
||||
```
|
||||
|
||||
**GitLab CI Example**
|
||||
```yaml
|
||||
test:
|
||||
image: koalaman/shellcheck-alpine
|
||||
script:
|
||||
- find . -name "*.sh" -exec shellcheck {} +
|
||||
|
||||
bats:
|
||||
image: bats/bats
|
||||
script:
|
||||
- bats test/
|
||||
```
|
||||
|
||||
### Code Coverage
|
||||
|
||||
**bashcov**
|
||||
- **URL:** https://github.com/infertux/bashcov
|
||||
- **Description:** Code coverage for bash
|
||||
- **Installation:** `gem install bashcov`
|
||||
- **Use for:** Measuring test coverage
|
||||
|
||||
---
|
||||
|
||||
## Platform-Specific Resources
|
||||
|
||||
### Linux
|
||||
|
||||
**Linux Man Pages**
|
||||
- **URL:** https://man7.org/linux/man-pages/
|
||||
- **Use for:** Linux-specific command documentation
|
||||
|
||||
**systemd Documentation**
|
||||
- **URL:** https://www.freedesktop.org/software/systemd/man/
|
||||
- **Use for:** systemd service management
|
||||
|
||||
### macOS
|
||||
|
||||
**macOS Man Pages**
|
||||
- **URL:** https://www.freebsd.org/cgi/man.cgi
|
||||
- **Description:** BSD-based commands (similar to macOS)
|
||||
- **Use for:** macOS command differences
|
||||
|
||||
**Homebrew**
|
||||
- **URL:** https://brew.sh/
|
||||
- **Use for:** Installing GNU tools on macOS
|
||||
|
||||
### Windows
|
||||
|
||||
**Git for Windows**
|
||||
- **URL:** https://gitforwindows.org/
|
||||
- **Documentation:** https://github.com/git-for-windows/git/wiki
|
||||
- **Use for:** Git Bash on Windows
|
||||
|
||||
**WSL Documentation**
|
||||
- **URL:** https://docs.microsoft.com/en-us/windows/wsl/
|
||||
- **Use for:** Windows Subsystem for Linux
|
||||
|
||||
**Cygwin**
|
||||
- **URL:** https://www.cygwin.com/
|
||||
- **Use for:** POSIX environment on Windows
|
||||
|
||||
### Containers
|
||||
|
||||
**Docker Bash Best Practices**
|
||||
- **URL:** https://docs.docker.com/develop/develop-images/dockerfile_best-practices/
|
||||
- **Use for:** Bash in containers
|
||||
|
||||
**Container Best Practices**
|
||||
- **URL:** https://cloud.google.com/architecture/best-practices-for-building-containers
|
||||
- **Use for:** Production container scripts
|
||||
|
||||
---
|
||||
|
||||
## Advanced Topics
|
||||
|
||||
### Process Substitution
|
||||
|
||||
**Greg's Wiki:**
|
||||
- **URL:** https://mywiki.wooledge.org/ProcessSubstitution
|
||||
- **Use for:** Understanding `<()` syntax
|
||||
|
||||
### Parameter Expansion
|
||||
|
||||
**Bash Hackers Wiki:**
|
||||
- **URL:** https://wiki.bash-hackers.org/syntax/pe
|
||||
- **Use for:** Complete parameter expansion reference
|
||||
|
||||
### Regular Expressions
|
||||
|
||||
**Bash Regex:**
|
||||
- **URL:** https://mywiki.wooledge.org/RegularExpression
|
||||
- **Use for:** Regex in bash `[[ =~ ]]`
|
||||
|
||||
**PCRE vs POSIX:**
|
||||
- **URL:** https://www.regular-expressions.info/posix.html
|
||||
- **Use for:** Understanding regex flavors
|
||||
|
||||
### Parallel Processing
|
||||
|
||||
**GNU Parallel:**
|
||||
- **URL:** https://www.gnu.org/software/parallel/
|
||||
- **Tutorial:** https://www.gnu.org/software/parallel/parallel_tutorial.html
|
||||
- **Use for:** Parallel command execution
|
||||
|
||||
### Job Control
|
||||
|
||||
**Bash Job Control:**
|
||||
- **URL:** https://www.gnu.org/software/bash/manual/html_node/Job-Control.html
|
||||
- **Use for:** Background jobs, job management
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting Resources
|
||||
|
||||
### Debugging Tools
|
||||
|
||||
**bashdb**
|
||||
- **URL:** http://bashdb.sourceforge.net/
|
||||
- **Description:** Bash debugger
|
||||
- **Use for:** Step-by-step debugging
|
||||
|
||||
**xtrace**
|
||||
```bash
|
||||
set -x # Enable
|
||||
set +x # Disable
|
||||
```
|
||||
- **Use for:** Trace command execution
|
||||
|
||||
**PS4 for Better Trace Output**
|
||||
```bash
|
||||
export PS4='+(${BASH_SOURCE}:${LINENO}): ${FUNCNAME[0]:+${FUNCNAME[0]}(): }'
|
||||
set -x
|
||||
```
|
||||
|
||||
### Common Issues
|
||||
|
||||
**Bash Pitfalls**
|
||||
- **URL:** https://mywiki.wooledge.org/BashPitfalls
|
||||
- **Description:** 50+ common mistakes in bash
|
||||
- **Use for:** Avoiding and fixing common errors
|
||||
|
||||
**Bash FAQ**
|
||||
- **URL:** https://mywiki.wooledge.org/BashFAQ
|
||||
- **Description:** Frequently asked questions
|
||||
- **Use for:** Quick answers to common questions
|
||||
|
||||
---
|
||||
|
||||
## Summary: Where to Find Information
|
||||
|
||||
| Question Type | Resource |
|
||||
|---------------|----------|
|
||||
| Syntax reference | Bash Manual, DevHints cheat sheet |
|
||||
| Best practices | Google Shell Style Guide, ShellCheck |
|
||||
| Portable scripting | POSIX specification, checkbashisms |
|
||||
| Quick examples | tldr, explainshell.com |
|
||||
| Common mistakes | Bash Pitfalls, ShellCheck Wiki |
|
||||
| Advanced topics | Bash Hackers Wiki, Greg's Wiki |
|
||||
| Testing | BATS documentation |
|
||||
| Platform differences | Platform-specific docs, Stack Overflow |
|
||||
| Troubleshooting | Stack Overflow, Unix & Linux SE |
|
||||
| Learning path | Bash Academy, TLDP guides |
|
||||
|
||||
---
|
||||
|
||||
## Quick Resource Lookup
|
||||
|
||||
**When writing a new script:**
|
||||
1. Start with template from Google Style Guide
|
||||
2. Use ShellCheck while developing
|
||||
3. Reference Bash Manual for specific features
|
||||
4. Check Bash Pitfalls for common mistakes
|
||||
|
||||
**When debugging:**
|
||||
1. Use `set -x` for tracing
|
||||
2. Check ShellCheck warnings
|
||||
3. Search Bash Pitfalls
|
||||
4. Search Stack Overflow for specific error
|
||||
|
||||
**When learning:**
|
||||
1. Start with Bash Academy or TLDP
|
||||
2. Use explainshell.com for commands
|
||||
3. Read Greg's Wiki for in-depth topics
|
||||
4. Practice with BATS tests
|
||||
|
||||
**When ensuring quality:**
|
||||
1. Run ShellCheck
|
||||
2. Run shellcheck
|
||||
3. Format with shfmt
|
||||
4. Write BATS tests
|
||||
5. Review against Google Style Guide
|
||||
|
||||
These resources provide authoritative, up-to-date information for all aspects of bash scripting.
|
||||
798
skills/bash-master/references/windows-git-bash-paths.md
Normal file
798
skills/bash-master/references/windows-git-bash-paths.md
Normal file
@@ -0,0 +1,798 @@
|
||||
# Windows Git Bash / MINGW Path Conversion & Shell Detection
|
||||
|
||||
**CRITICAL KNOWLEDGE FOR BASH SCRIPTING ON WINDOWS**
|
||||
|
||||
This reference provides comprehensive guidance for handling path conversion and shell detection in Git Bash/MINGW/MSYS2 environments on Windows - essential knowledge for cross-platform bash scripting.
|
||||
|
||||
---
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [Path Conversion in Git Bash/MINGW](#path-conversion-in-git-bashMINGW)
|
||||
2. [Shell Detection Methods](#shell-detection-methods)
|
||||
3. [Claude Code Specific Issues](#claude-code-specific-issues)
|
||||
4. [Practical Solutions](#practical-solutions)
|
||||
5. [Best Practices](#best-practices)
|
||||
|
||||
---
|
||||
|
||||
## Path Conversion in Git Bash/MINGW
|
||||
|
||||
### Automatic Conversion Behavior
|
||||
|
||||
Git Bash/MINGW automatically converts Unix-style paths to Windows paths when passing arguments to native Windows programs. Understanding this behavior is critical for writing portable scripts.
|
||||
|
||||
**Conversion Rules:**
|
||||
|
||||
```bash
|
||||
# Unix → Windows path conversion
|
||||
/foo → C:/Program Files/Git/usr/foo
|
||||
|
||||
# Path lists (colon-separated → semicolon-separated)
|
||||
/foo:/bar → C:\msys64\foo;C:\msys64\bar
|
||||
|
||||
# Arguments with paths
|
||||
--dir=/foo → --dir=C:/msys64/foo
|
||||
```
|
||||
|
||||
### What Triggers Conversion
|
||||
|
||||
Automatic path conversion is triggered by:
|
||||
|
||||
```bash
|
||||
# ✓ Leading forward slash (/) in arguments
|
||||
command /c/Users/username/file.txt
|
||||
|
||||
# ✓ Colon-separated path lists
|
||||
export PATH=/usr/bin:/usr/local/bin
|
||||
|
||||
# ✓ Arguments after - or , with path components
|
||||
command --path=/tmp/data
|
||||
```
|
||||
|
||||
### What's Exempt from Conversion
|
||||
|
||||
These patterns do NOT trigger automatic conversion:
|
||||
|
||||
```bash
|
||||
# ✓ Arguments containing = (variable assignments)
|
||||
VAR=/path/to/something
|
||||
|
||||
# ✓ Drive specifiers (C:)
|
||||
C:/Windows/System32
|
||||
|
||||
# ✓ Arguments with ; (already Windows format)
|
||||
PATH=C:\foo;C:\bar
|
||||
|
||||
# ✓ Arguments starting with // (Windows switches or UNC paths)
|
||||
//server/share
|
||||
command //e //s # Command-line switches
|
||||
```
|
||||
|
||||
### Control Environment Variables
|
||||
|
||||
**MSYS_NO_PATHCONV** (Git for Windows only):
|
||||
|
||||
```bash
|
||||
# Disable ALL path conversion
|
||||
export MSYS_NO_PATHCONV=1
|
||||
command /path/to/file
|
||||
|
||||
# Per-command usage (recommended)
|
||||
MSYS_NO_PATHCONV=1 command /path/to/file
|
||||
|
||||
# Value doesn't matter, just needs to be defined
|
||||
MSYS_NO_PATHCONV=0 # Still disables conversion
|
||||
```
|
||||
|
||||
**MSYS2_ARG_CONV_EXCL** (MSYS2 only):
|
||||
|
||||
```bash
|
||||
# Exclude everything
|
||||
export MSYS2_ARG_CONV_EXCL="*"
|
||||
|
||||
# Exclude specific prefixes
|
||||
export MSYS2_ARG_CONV_EXCL="--dir=;/test"
|
||||
|
||||
# Multiple patterns (semicolon-separated)
|
||||
export MSYS2_ARG_CONV_EXCL="--path=;--config=;/tmp"
|
||||
```
|
||||
|
||||
**MSYS2_ENV_CONV_EXCL**:
|
||||
|
||||
```bash
|
||||
# Prevents environment variable conversion
|
||||
# Same syntax as MSYS2_ARG_CONV_EXCL
|
||||
export MSYS2_ENV_CONV_EXCL="MY_PATH;CONFIG_DIR"
|
||||
```
|
||||
|
||||
### Manual Conversion with cygpath
|
||||
|
||||
The `cygpath` utility provides precise control over path conversion:
|
||||
|
||||
```bash
|
||||
# Convert Windows → Unix format
|
||||
unix_path=$(cygpath -u "C:\Users\username\file.txt")
|
||||
# Result: /c/Users/username/file.txt
|
||||
|
||||
# Convert Unix → Windows format
|
||||
windows_path=$(cygpath -w "/c/Users/username/file.txt")
|
||||
# Result: C:\Users\username\file.txt
|
||||
|
||||
# Convert to mixed format (forward slashes, Windows drive)
|
||||
mixed_path=$(cygpath -m "/c/Users/username/file.txt")
|
||||
# Result: C:/Users/username/file.txt
|
||||
|
||||
# Convert absolute path
|
||||
absolute_path=$(cygpath -a "relative/path")
|
||||
|
||||
# Convert multiple paths
|
||||
cygpath -u "C:\path1" "C:\path2"
|
||||
```
|
||||
|
||||
**Practical cygpath usage:**
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
# Cross-platform path handling
|
||||
|
||||
get_native_path() {
|
||||
local path="$1"
|
||||
|
||||
# Check if running on Windows (Git Bash/MINGW)
|
||||
if [[ "$OSTYPE" == "msys" ]] || [[ "$OSTYPE" == "mingw"* ]]; then
|
||||
# Convert to Windows format for native programs
|
||||
cygpath -w "$path"
|
||||
else
|
||||
# Already Unix format on Linux/macOS
|
||||
echo "$path"
|
||||
fi
|
||||
}
|
||||
|
||||
# Usage
|
||||
native_path=$(get_native_path "/c/Users/data")
|
||||
windows_program.exe "$native_path"
|
||||
```
|
||||
|
||||
### Common Workarounds
|
||||
|
||||
When automatic conversion causes issues:
|
||||
|
||||
**1. Use double slashes:**
|
||||
|
||||
```bash
|
||||
# Problem: /e gets converted to C:/Program Files/Git/e
|
||||
command /e /s
|
||||
|
||||
# Solution: Use double slashes
|
||||
command //e //s # Treated as switches, not paths
|
||||
```
|
||||
|
||||
**2. Use dash notation:**
|
||||
|
||||
```bash
|
||||
# Problem: /e flag converted to path
|
||||
command /e /s
|
||||
|
||||
# Solution: Use dash notation
|
||||
command -e -s
|
||||
```
|
||||
|
||||
**3. Set MSYS_NO_PATHCONV temporarily:**
|
||||
|
||||
```bash
|
||||
# Disable conversion for single command
|
||||
MSYS_NO_PATHCONV=1 command /path/with/special/chars
|
||||
|
||||
# Or export for script section
|
||||
export MSYS_NO_PATHCONV=1
|
||||
command1 /path1
|
||||
command2 /path2
|
||||
unset MSYS_NO_PATHCONV
|
||||
```
|
||||
|
||||
**4. Quote paths with spaces:**
|
||||
|
||||
```bash
|
||||
# Always quote paths with spaces
|
||||
command "/c/Program Files/App/file.txt"
|
||||
|
||||
# Or escape spaces
|
||||
command /c/Program\ Files/App/file.txt
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Shell Detection Methods
|
||||
|
||||
### Method 1: $OSTYPE (Fastest, Bash-Only)
|
||||
|
||||
Best for: Quick platform detection in bash scripts
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
|
||||
case "$OSTYPE" in
|
||||
linux-gnu*)
|
||||
echo "Linux"
|
||||
;;
|
||||
darwin*)
|
||||
echo "macOS"
|
||||
;;
|
||||
cygwin*)
|
||||
echo "Cygwin"
|
||||
;;
|
||||
msys*)
|
||||
echo "MSYS/Git Bash/MinGW"
|
||||
# Most common in Git for Windows
|
||||
;;
|
||||
win*)
|
||||
echo "Windows (native)"
|
||||
;;
|
||||
*)
|
||||
echo "Unknown: $OSTYPE"
|
||||
;;
|
||||
esac
|
||||
```
|
||||
|
||||
**Advantages:**
|
||||
- Fast (shell variable, no external command)
|
||||
- Reliable for bash
|
||||
- No forking required
|
||||
|
||||
**Disadvantages:**
|
||||
- Bash-specific (not available in POSIX sh)
|
||||
- Less detailed than uname
|
||||
|
||||
### Method 2: uname -s (Most Portable)
|
||||
|
||||
Best for: Maximum portability and detailed information
|
||||
|
||||
```bash
|
||||
#!/bin/sh
|
||||
# Works in any POSIX shell
|
||||
|
||||
case "$(uname -s)" in
|
||||
Darwin*)
|
||||
echo "macOS"
|
||||
;;
|
||||
Linux*)
|
||||
# Check for WSL
|
||||
if grep -qi microsoft /proc/version 2>/dev/null; then
|
||||
echo "Windows Subsystem for Linux (WSL)"
|
||||
else
|
||||
echo "Linux (native)"
|
||||
fi
|
||||
;;
|
||||
CYGWIN*)
|
||||
echo "Cygwin"
|
||||
;;
|
||||
MINGW64*)
|
||||
echo "Git Bash 64-bit / MINGW64"
|
||||
;;
|
||||
MINGW32*)
|
||||
echo "Git Bash 32-bit / MINGW32"
|
||||
;;
|
||||
MSYS_NT*)
|
||||
echo "MSYS"
|
||||
;;
|
||||
*)
|
||||
echo "Unknown: $(uname -s)"
|
||||
;;
|
||||
esac
|
||||
```
|
||||
|
||||
**Common uname -s outputs:**
|
||||
|
||||
| Output | Platform | Description |
|
||||
|--------|----------|-------------|
|
||||
| `Darwin` | macOS | All macOS versions |
|
||||
| `Linux` | Linux/WSL | Check `/proc/version` for WSL |
|
||||
| `MINGW64_NT-10.0-*` | Git Bash | Git for Windows (64-bit) |
|
||||
| `MINGW32_NT-10.0-*` | Git Bash | Git for Windows (32-bit) |
|
||||
| `CYGWIN_NT-*` | Cygwin | Cygwin environment |
|
||||
| `MSYS_NT-*` | MSYS | MSYS environment |
|
||||
|
||||
**Advantages:**
|
||||
- Works in any POSIX shell
|
||||
- Detailed system information
|
||||
- Standard on all Unix-like systems
|
||||
|
||||
**Disadvantages:**
|
||||
- Requires forking (slower than $OSTYPE)
|
||||
- Output format varies by OS version
|
||||
|
||||
### Method 3: $MSYSTEM (MSYS2/Git Bash Specific)
|
||||
|
||||
Best for: Detecting MINGW subsystem type
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
|
||||
case "$MSYSTEM" in
|
||||
MINGW64)
|
||||
echo "Native Windows 64-bit environment"
|
||||
# Build native Windows 64-bit applications
|
||||
;;
|
||||
MINGW32)
|
||||
echo "Native Windows 32-bit environment"
|
||||
# Build native Windows 32-bit applications
|
||||
;;
|
||||
MSYS)
|
||||
echo "POSIX-compliant environment"
|
||||
# Build POSIX applications (depend on msys-2.0.dll)
|
||||
;;
|
||||
"")
|
||||
echo "Not running in MSYS2/Git Bash"
|
||||
;;
|
||||
*)
|
||||
echo "Unknown MSYSTEM: $MSYSTEM"
|
||||
;;
|
||||
esac
|
||||
```
|
||||
|
||||
**MSYSTEM Values:**
|
||||
|
||||
| Value | Purpose | Path Conversion | Libraries |
|
||||
|-------|---------|-----------------|-----------|
|
||||
| `MINGW64` | Native Windows 64-bit | Automatic | Windows native (mingw-w64) |
|
||||
| `MINGW32` | Native Windows 32-bit | Automatic | Windows native (mingw) |
|
||||
| `MSYS` | POSIX environment | Minimal | POSIX (msys-2.0.dll) |
|
||||
|
||||
**WARNING:** Never set `$MSYSTEM` manually outside of MSYS2/Git Bash shells! It's automatically set by the environment and changing it can break the system.
|
||||
|
||||
**Advantages:**
|
||||
- Precise subsystem detection
|
||||
- Important for build systems
|
||||
- Fast (environment variable)
|
||||
|
||||
**Disadvantages:**
|
||||
- Only available in MSYS2/Git Bash
|
||||
- Not set on other platforms
|
||||
|
||||
### Comprehensive Detection Function
|
||||
|
||||
Combine all methods for robust detection:
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
|
||||
detect_platform() {
|
||||
local platform=""
|
||||
local details=""
|
||||
|
||||
# Check MSYSTEM first (most specific for Git Bash)
|
||||
if [[ -n "${MSYSTEM:-}" ]]; then
|
||||
platform="gitbash"
|
||||
details="$MSYSTEM"
|
||||
echo "platform=$platform subsystem=$MSYSTEM"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Check OSTYPE
|
||||
case "$OSTYPE" in
|
||||
linux-gnu*)
|
||||
# Distinguish WSL from native Linux
|
||||
if grep -qi microsoft /proc/version 2>/dev/null; then
|
||||
platform="wsl"
|
||||
if [[ -n "${WSL_DISTRO_NAME:-}" ]]; then
|
||||
details="$WSL_DISTRO_NAME"
|
||||
fi
|
||||
else
|
||||
platform="linux"
|
||||
fi
|
||||
;;
|
||||
darwin*)
|
||||
platform="macos"
|
||||
;;
|
||||
msys*|mingw*|cygwin*)
|
||||
platform="gitbash"
|
||||
;;
|
||||
*)
|
||||
# Fallback to uname
|
||||
case "$(uname -s 2>/dev/null)" in
|
||||
MINGW*|MSYS*)
|
||||
platform="gitbash"
|
||||
;;
|
||||
CYGWIN*)
|
||||
platform="cygwin"
|
||||
;;
|
||||
*)
|
||||
platform="unknown"
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
esac
|
||||
|
||||
echo "platform=$platform${details:+ details=$details}"
|
||||
}
|
||||
|
||||
# Usage
|
||||
platform_info=$(detect_platform)
|
||||
echo "$platform_info"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Claude Code Specific Issues
|
||||
|
||||
### Issue #2602: Snapshot Path Conversion Failure
|
||||
|
||||
**Problem:**
|
||||
```
|
||||
/usr/bin/bash: line 1: C:UsersDavid...No such file
|
||||
```
|
||||
|
||||
**Root Cause:**
|
||||
- Node.js `os.tmpdir()` returns Windows paths (e.g., `C:\Users\...`)
|
||||
- Git Bash expects Unix paths (e.g., `/c/Users/...`)
|
||||
- Automatic conversion fails due to path format mismatch
|
||||
|
||||
**Solution (Claude Code v1.0.51+):**
|
||||
|
||||
Set environment variable before starting Claude Code:
|
||||
|
||||
```powershell
|
||||
# PowerShell
|
||||
$env:CLAUDE_CODE_GIT_BASH_PATH = "C:\Program Files\git\bin\bash.exe"
|
||||
```
|
||||
|
||||
```cmd
|
||||
# CMD
|
||||
set CLAUDE_CODE_GIT_BASH_PATH=C:\Program Files\git\bin\bash.exe
|
||||
```
|
||||
|
||||
```bash
|
||||
# Git Bash (add to ~/.bashrc)
|
||||
export CLAUDE_CODE_GIT_BASH_PATH="C:\\Program Files\\git\\bin\\bash.exe"
|
||||
```
|
||||
|
||||
**Note:** Versions 1.0.72+ reportedly work without modifications, but setting the environment variable ensures compatibility.
|
||||
|
||||
### Other Known Issues
|
||||
|
||||
**Drive letter duplication:**
|
||||
```bash
|
||||
# Problem
|
||||
cd D:\dev
|
||||
pwd
|
||||
# Output: D:\d\dev (incorrect)
|
||||
|
||||
# Solution: Use Unix-style path in Git Bash
|
||||
cd /d/dev
|
||||
pwd
|
||||
# Output: /d/dev
|
||||
```
|
||||
|
||||
**Spaces in paths:**
|
||||
```bash
|
||||
# Problem: Unquoted path with spaces
|
||||
cd C:\Program Files\App # Fails
|
||||
|
||||
# Solution: Always quote paths with spaces
|
||||
cd "C:\Program Files\App"
|
||||
cd /c/Program\ Files/App
|
||||
```
|
||||
|
||||
**VS Code extension Git Bash detection:**
|
||||
|
||||
VS Code may not auto-detect Git Bash. Configure manually in settings:
|
||||
|
||||
```json
|
||||
{
|
||||
"terminal.integrated.defaultProfile.windows": "Git Bash",
|
||||
"terminal.integrated.profiles.windows": {
|
||||
"Git Bash": {
|
||||
"path": "C:\\Program Files\\Git\\bin\\bash.exe"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Practical Solutions
|
||||
|
||||
### Cross-Platform Path Handling Function
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Convert path to format appropriate for current platform
|
||||
normalize_path_for_platform() {
|
||||
local path="$1"
|
||||
|
||||
case "$OSTYPE" in
|
||||
msys*|mingw*)
|
||||
# On Git Bash, convert to Unix format if Windows format provided
|
||||
if [[ "$path" =~ ^[A-Z]:\\ ]]; then
|
||||
# Windows path detected, convert to Unix
|
||||
path=$(cygpath -u "$path" 2>/dev/null || echo "$path")
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
# On Linux/macOS, path is already correct
|
||||
;;
|
||||
esac
|
||||
|
||||
echo "$path"
|
||||
}
|
||||
|
||||
# Convert path to native format for external programs
|
||||
convert_to_native_path() {
|
||||
local path="$1"
|
||||
|
||||
case "$OSTYPE" in
|
||||
msys*|mingw*)
|
||||
# Convert to Windows format for native Windows programs
|
||||
cygpath -w "$path" 2>/dev/null || echo "$path"
|
||||
;;
|
||||
*)
|
||||
# Already native on Linux/macOS
|
||||
echo "$path"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Example usage
|
||||
input_path="/c/Users/username/file.txt"
|
||||
normalized=$(normalize_path_for_platform "$input_path")
|
||||
echo "Normalized: $normalized"
|
||||
|
||||
native=$(convert_to_native_path "$normalized")
|
||||
echo "Native: $native"
|
||||
```
|
||||
|
||||
### Script Template for Windows Compatibility
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Detect if running on Git Bash/MINGW
|
||||
is_git_bash() {
|
||||
[[ "$OSTYPE" == "msys" ]] || [[ "$OSTYPE" == "mingw"* ]]
|
||||
}
|
||||
|
||||
# Handle path conversion based on platform
|
||||
get_path() {
|
||||
local path="$1"
|
||||
|
||||
if is_git_bash; then
|
||||
# Ensure Unix format in Git Bash
|
||||
if [[ "$path" =~ ^[A-Z]:\\ ]]; then
|
||||
cygpath -u "$path"
|
||||
else
|
||||
echo "$path"
|
||||
fi
|
||||
else
|
||||
echo "$path"
|
||||
fi
|
||||
}
|
||||
|
||||
# Call Windows program from Git Bash
|
||||
call_windows_program() {
|
||||
local program="$1"
|
||||
shift
|
||||
local args=("$@")
|
||||
|
||||
if is_git_bash; then
|
||||
# Disable path conversion for complex arguments
|
||||
MSYS_NO_PATHCONV=1 "$program" "${args[@]}"
|
||||
else
|
||||
"$program" "${args[@]}"
|
||||
fi
|
||||
}
|
||||
|
||||
# Main script logic
|
||||
main() {
|
||||
local file_path="$1"
|
||||
|
||||
# Normalize path
|
||||
file_path=$(get_path "$file_path")
|
||||
|
||||
# Process file
|
||||
echo "Processing: $file_path"
|
||||
|
||||
# Call Windows program if needed
|
||||
if is_git_bash; then
|
||||
local native_path
|
||||
native_path=$(cygpath -w "$file_path")
|
||||
call_windows_program notepad.exe "$native_path"
|
||||
fi
|
||||
}
|
||||
|
||||
main "$@"
|
||||
```
|
||||
|
||||
### Handling Command-Line Arguments
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Parse arguments that might contain paths
|
||||
parse_arguments() {
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--path=*)
|
||||
local path="${1#*=}"
|
||||
# Disable conversion for this specific argument pattern
|
||||
MSYS2_ARG_CONV_EXCL="--path=" command --path="$path"
|
||||
shift
|
||||
;;
|
||||
--dir)
|
||||
local dir="$2"
|
||||
# Use converted path
|
||||
local native_dir
|
||||
if command -v cygpath &>/dev/null; then
|
||||
native_dir=$(cygpath -w "$dir")
|
||||
else
|
||||
native_dir="$dir"
|
||||
fi
|
||||
command --dir "$native_dir"
|
||||
shift 2
|
||||
;;
|
||||
*)
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Best Practices
|
||||
|
||||
### 1. Always Quote Paths
|
||||
|
||||
```bash
|
||||
# ✗ WRONG - Breaks with spaces
|
||||
cd $path
|
||||
|
||||
# ✓ CORRECT - Works with all paths
|
||||
cd "$path"
|
||||
```
|
||||
|
||||
### 2. Use cygpath for Reliable Conversion
|
||||
|
||||
```bash
|
||||
# ✗ WRONG - Manual conversion is error-prone
|
||||
path="${path//\\/\/}"
|
||||
path="${path/C:/\/c}"
|
||||
|
||||
# ✓ CORRECT - Use cygpath
|
||||
path=$(cygpath -u "$path")
|
||||
```
|
||||
|
||||
### 3. Detect Platform Before Path Operations
|
||||
|
||||
```bash
|
||||
# ✓ CORRECT - Platform-aware
|
||||
if [[ "$OSTYPE" == "msys" ]] || [[ "$OSTYPE" == "mingw"* ]]; then
|
||||
# Git Bash specific handling
|
||||
path=$(cygpath -u "$windows_path")
|
||||
else
|
||||
# Linux/macOS handling
|
||||
path="$unix_path"
|
||||
fi
|
||||
```
|
||||
|
||||
### 4. Use MSYS_NO_PATHCONV Sparingly
|
||||
|
||||
```bash
|
||||
# ✗ WRONG - Disables all conversion globally
|
||||
export MSYS_NO_PATHCONV=1
|
||||
|
||||
# ✓ CORRECT - Per-command when needed
|
||||
MSYS_NO_PATHCONV=1 command --flag=/value
|
||||
```
|
||||
|
||||
### 5. Test on Target Platform
|
||||
|
||||
Always test scripts on Windows with Git Bash if that's a target platform:
|
||||
|
||||
```bash
|
||||
# Test script
|
||||
bash -n script.sh # Syntax check
|
||||
shellcheck script.sh # Static analysis
|
||||
bash script.sh # Run on actual platform
|
||||
```
|
||||
|
||||
### 6. Document Platform Requirements
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Platform Support:
|
||||
# - Linux: Full support
|
||||
# - macOS: Full support
|
||||
# - Windows Git Bash: Requires Git for Windows 2.x+
|
||||
# - Windows WSL: Full support
|
||||
#
|
||||
# Known Issues:
|
||||
# - Path conversion may occur when calling Windows programs from Git Bash
|
||||
# - Use MSYS_NO_PATHCONV=1 if experiencing path-related errors
|
||||
```
|
||||
|
||||
### 7. Use Forward Slashes in Git Bash
|
||||
|
||||
```bash
|
||||
# ✓ PREFERRED - Works in all environments
|
||||
cd /c/Users/username/project
|
||||
|
||||
# ✗ AVOID - Requires escaping or quoting
|
||||
cd "C:\Users\username\project"
|
||||
cd C:\\Users\\username\\project
|
||||
```
|
||||
|
||||
### 8. Check for cygpath Availability
|
||||
|
||||
```bash
|
||||
# Graceful fallback if cygpath not available
|
||||
convert_path() {
|
||||
local path="$1"
|
||||
|
||||
if command -v cygpath &>/dev/null; then
|
||||
cygpath -u "$path"
|
||||
else
|
||||
# Manual conversion as fallback
|
||||
echo "$path" | sed 's|\\|/|g' | sed 's|^\([A-Z]\):|/\L\1|'
|
||||
fi
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Quick Reference Card
|
||||
|
||||
### Path Conversion Control
|
||||
|
||||
| Variable | Scope | Effect |
|
||||
|----------|-------|--------|
|
||||
| `MSYS_NO_PATHCONV=1` | Git for Windows | Disables all conversion |
|
||||
| `MSYS2_ARG_CONV_EXCL="pattern"` | MSYS2 | Excludes specific patterns |
|
||||
| `MSYS2_ENV_CONV_EXCL="var"` | MSYS2 | Excludes environment variables |
|
||||
|
||||
### Shell Detection Variables
|
||||
|
||||
| Variable | Available | Purpose |
|
||||
|----------|-----------|---------|
|
||||
| `$OSTYPE` | Bash | Quick OS type detection |
|
||||
| `$MSYSTEM` | MSYS2/Git Bash | Subsystem type (MINGW64/MINGW32/MSYS) |
|
||||
| `$(uname -s)` | All POSIX | Detailed OS identification |
|
||||
|
||||
### cygpath Quick Reference
|
||||
|
||||
| Command | Purpose |
|
||||
|---------|---------|
|
||||
| `cygpath -u "C:\path"` | Windows → Unix format |
|
||||
| `cygpath -w "/c/path"` | Unix → Windows format |
|
||||
| `cygpath -m "/c/path"` | Unix → Mixed format (forward slashes) |
|
||||
| `cygpath -a "path"` | Convert to absolute path |
|
||||
|
||||
### Common Issues & Solutions
|
||||
|
||||
| Problem | Solution |
|
||||
|---------|----------|
|
||||
| Path with spaces breaks | Quote the path: `"$path"` |
|
||||
| Flag `/e` converted to path | Use `//e` or `-e` instead |
|
||||
| Drive duplication `D:\d\` | Use Unix format: `/d/` |
|
||||
| Windows program needs Windows path | Use `cygpath -w "$unix_path"` |
|
||||
| Script fails in Claude Code | Set `CLAUDE_CODE_GIT_BASH_PATH` |
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
Understanding Git Bash/MINGW path conversion is essential for writing robust cross-platform bash scripts that work on Windows. Key takeaways:
|
||||
|
||||
1. **Automatic conversion** happens for Unix-style paths in arguments
|
||||
2. **Control conversion** using `MSYS_NO_PATHCONV` and `MSYS2_ARG_CONV_EXCL`
|
||||
3. **Use cygpath** for reliable manual path conversion
|
||||
4. **Detect platform** using `$OSTYPE`, `$MSYSTEM`, or `uname -s`
|
||||
5. **Quote all paths** to handle spaces and special characters
|
||||
6. **Test on target platforms** to catch platform-specific issues
|
||||
7. **Document requirements** so users know what to expect
|
||||
|
||||
With this knowledge, you can write bash scripts that work seamlessly across Linux, macOS, Windows Git Bash, WSL, and other Unix-like environments.
|
||||
Reference in New Issue
Block a user