first commit
This commit is contained in:
307
文档润色流和知识库构建流/claude-scholar/skills/bug-detective/SKILL.md
Normal file
307
文档润色流和知识库构建流/claude-scholar/skills/bug-detective/SKILL.md
Normal file
@@ -0,0 +1,307 @@
|
||||
---
|
||||
name: bug-detective
|
||||
description: This skill should be used when the user asks to "debug this", "fix this error", "investigate this bug", "troubleshoot this issue", "find the problem", "something is broken", "this isn't working", "why is this failing", or reports errors/exceptions/bugs. Provides systematic debugging workflow and common error patterns.
|
||||
version: 0.1.0
|
||||
---
|
||||
|
||||
# Bug Detective
|
||||
|
||||
A systematic debugging workflow for investigating and resolving code errors, exceptions, and failures. Provides structured debugging methods and common error pattern recognition.
|
||||
|
||||
## Core Philosophy
|
||||
|
||||
Debugging is a scientific problem-solving process that requires:
|
||||
1. **Understand the problem** - Clearly define symptoms and expected behavior
|
||||
2. **Gather evidence** - Collect error messages, logs, stack traces
|
||||
3. **Form hypotheses** - Infer possible causes based on evidence
|
||||
4. **Verify hypotheses** - Confirm or eliminate causes through experiments
|
||||
5. **Resolve the issue** - Apply fixes and verify
|
||||
|
||||
## Debugging Workflow
|
||||
|
||||
### Step 1: Understand the Problem
|
||||
|
||||
Before starting to debug, clarify the following information:
|
||||
|
||||
**Required information to collect:**
|
||||
- Complete error message content
|
||||
- Exact location of the error (filename and line number)
|
||||
- Reproduction steps (how to trigger the error)
|
||||
- Expected behavior vs actual behavior
|
||||
- Environment info (OS, versions, dependencies)
|
||||
|
||||
**Question template:**
|
||||
```
|
||||
1. What is the exact error message?
|
||||
2. Which file and line does the error occur at?
|
||||
3. How can this issue be reproduced? Provide detailed steps.
|
||||
4. What was the expected result? What actually happened?
|
||||
5. What recent changes might have introduced this issue?
|
||||
```
|
||||
|
||||
### Step 2: Analyze Error Type
|
||||
|
||||
Choose a debugging strategy based on error type:
|
||||
|
||||
| Error Type | Characteristics | Debugging Method |
|
||||
|-----------|----------------|-----------------|
|
||||
| **Syntax Error** | Code cannot be parsed | Check syntax, bracket matching, quotes |
|
||||
| **Import Error** | ModuleNotFoundError | Check module installation, path config |
|
||||
| **Type Error** | TypeError | Check data types, type conversions |
|
||||
| **Attribute Error** | AttributeError | Check if object attribute exists |
|
||||
| **Key Error** | KeyError | Check if dictionary key exists |
|
||||
| **Index Error** | IndexError | Check list/array index range |
|
||||
| **Null Reference** | NoneType/NullPointerException | Check if variable is None |
|
||||
| **Network Error** | ConnectionError/Timeout | Check network connection, URL, timeout settings |
|
||||
| **Permission Error** | PermissionError | Check file permissions, user permissions |
|
||||
| **Resource Error** | FileNotFoundError | Check if file path exists |
|
||||
|
||||
### Step 3: Locate the Problem Source
|
||||
|
||||
Use the following methods to locate the issue:
|
||||
|
||||
**1. Binary Search Method**
|
||||
- Comment out half the code, check if the problem persists
|
||||
- Progressively narrow the scope until the problematic code is found
|
||||
|
||||
**2. Log Tracing**
|
||||
- Add print/logging statements at key locations
|
||||
- Track variable value changes
|
||||
- Confirm code execution path
|
||||
|
||||
**3. Breakpoint Debugging**
|
||||
- Use debugger breakpoint functionality
|
||||
- Step through code execution
|
||||
- Inspect variable state
|
||||
|
||||
**4. Stack Trace Analysis**
|
||||
- Find the call chain from the stack trace in the error message
|
||||
- Determine the direct cause of the error
|
||||
- Trace back to the root cause
|
||||
|
||||
### Step 4: Form and Verify Hypotheses
|
||||
|
||||
**Hypothesis framework:**
|
||||
```
|
||||
Hypothesis: [problem description] causes [error phenomenon]
|
||||
|
||||
Verification steps:
|
||||
1. [verification method 1]
|
||||
2. [verification method 2]
|
||||
|
||||
Expected results:
|
||||
- If hypothesis is correct: [expected phenomenon]
|
||||
- If hypothesis is wrong: [expected phenomenon]
|
||||
```
|
||||
|
||||
### Step 5: Apply Fix
|
||||
|
||||
After fixing, verify:
|
||||
1. The original error is resolved
|
||||
2. No new errors have been introduced
|
||||
3. Related functionality still works correctly
|
||||
4. Tests added to prevent regression
|
||||
|
||||
## Python Common Error Patterns
|
||||
|
||||
### 1. Indentation Errors
|
||||
### 2. Mutable Default Arguments
|
||||
### 3. Closure Issues in Loops
|
||||
### 4. Modifying a List While Iterating
|
||||
### 5. Using `is` for String Comparison
|
||||
### 6. Forgetting to Call `super().__init__()`
|
||||
|
||||
## JavaScript/TypeScript Common Error Patterns
|
||||
|
||||
### 1. `this` Binding Issues
|
||||
### 2. Async Error Handling
|
||||
### 3. Object Reference Comparison
|
||||
|
||||
## Bash/Zsh Common Error Patterns
|
||||
|
||||
### 1. Spacing Issues
|
||||
|
||||
```bash
|
||||
# ❌ No spaces allowed in assignment
|
||||
name = "John" # Error: tries to run 'name' command
|
||||
|
||||
# ✅ Correct assignment
|
||||
name="John"
|
||||
|
||||
# ❌ Missing spaces in conditional test
|
||||
if[$name -eq 1]; then # Error
|
||||
|
||||
# ✅ Correct
|
||||
if [ $name -eq 1 ]; then
|
||||
```
|
||||
|
||||
### 2. Quoting Issues
|
||||
|
||||
```bash
|
||||
# ❌ Variables not expanded inside single quotes
|
||||
echo 'The value is $var' # Output: The value is $var
|
||||
|
||||
# ✅ Use double quotes
|
||||
echo "The value is $var" # Output: The value is actual_value
|
||||
|
||||
# ❌ Using backticks for command substitution (confusing)
|
||||
result=`command`
|
||||
|
||||
# ✅ Use $()
|
||||
result=$(command)
|
||||
```
|
||||
|
||||
### 3. Unquoted Variables
|
||||
|
||||
```bash
|
||||
# ❌ Unquoted variable, empty value causes errors
|
||||
rm -rf $dir/* # If dir is empty, deletes all files in current directory
|
||||
|
||||
# ✅ Always quote variables
|
||||
[ -n "$dir" ] && rm -rf "$dir"/*
|
||||
|
||||
# Or use set -u to prevent undefined variables
|
||||
set -u # or set -o nounset
|
||||
```
|
||||
|
||||
### 4. Variable Scope in Loops
|
||||
|
||||
```bash
|
||||
# ❌ Pipe creates subshell, outer variable unchanged
|
||||
cat file.txt | while read line; do
|
||||
count=$((count + 1)) # Outer count won't change
|
||||
done
|
||||
echo "Total: $count" # Outputs 0
|
||||
|
||||
# ✅ Use process substitution or redirection
|
||||
while read line; do
|
||||
count=$((count + 1))
|
||||
done < file.txt
|
||||
echo "Total: $count" # Correct output
|
||||
```
|
||||
|
||||
### 5. Array Operations
|
||||
|
||||
```bash
|
||||
# ❌ Incorrect array access
|
||||
arr=(1 2 3)
|
||||
echo $arr[1] # Outputs 1[1]
|
||||
|
||||
# ✅ Correct array access
|
||||
echo ${arr[1]} # Outputs 2
|
||||
echo ${arr[@]} # Outputs all elements
|
||||
echo ${#arr[@]} # Outputs array length
|
||||
```
|
||||
|
||||
### 6. String Comparison
|
||||
|
||||
```bash
|
||||
# ✅ Use `=` inside POSIX `[` tests and `==` inside Bash `[[ ]]` tests
|
||||
if [ "$name" = "John" ]; then
|
||||
if [[ "$name" == "John" ]]; then
|
||||
|
||||
# ❌ Using -eq for numeric comparison instead of =
|
||||
if [ $age = 18 ]; then # Wrong
|
||||
|
||||
# ✅ Use arithmetic operators for numeric comparison
|
||||
if [ $age -eq 18 ]; then
|
||||
if (( age == 18 )); then
|
||||
```
|
||||
|
||||
### 7. Command Failure Continues Execution
|
||||
|
||||
```bash
|
||||
# ❌ Execution continues after command failure
|
||||
cd /nonexistent
|
||||
rm file.txt # Deletes file.txt in current directory
|
||||
|
||||
# ✅ Use set -e to exit on error
|
||||
set -e # or set -o errexit
|
||||
cd /nonexistent # Script exits here
|
||||
rm file.txt
|
||||
|
||||
# Or check if command succeeded
|
||||
cd /nonexistent || exit 1
|
||||
```
|
||||
|
||||
## Common Debugging Commands
|
||||
|
||||
### Python pdb Debugger
|
||||
```bash
|
||||
python -m pdb script.py
|
||||
pytest -x -vv tests/test_target.py
|
||||
```
|
||||
|
||||
### Node.js Inspector
|
||||
```bash
|
||||
node --inspect-brk app.js
|
||||
node --trace-warnings app.js
|
||||
```
|
||||
|
||||
### Git Bisect
|
||||
```bash
|
||||
git bisect start
|
||||
git bisect bad
|
||||
git bisect good <known-good-commit>
|
||||
```
|
||||
|
||||
### Bash Debugging
|
||||
|
||||
```bash
|
||||
# Run script in debug mode
|
||||
bash -x script.sh # Print each command
|
||||
bash -v script.sh # Print command source
|
||||
bash -n script.sh # Syntax check, no execution
|
||||
|
||||
# Enable debugging within a script
|
||||
set -x # Enable command tracing
|
||||
set -v # Enable verbose mode
|
||||
set -e # Exit on error
|
||||
set -u # Error on undefined variables
|
||||
set -o pipefail # Fail if any command in pipe fails
|
||||
```
|
||||
|
||||
## Preventive Debugging
|
||||
|
||||
### 1. Use Type Checking
|
||||
### 2. Input Validation
|
||||
### 3. Defensive Programming
|
||||
### 4. Logging
|
||||
|
||||
## Debugging Checklist
|
||||
|
||||
### Before Starting
|
||||
- [ ] Obtain the complete error message
|
||||
- [ ] Record the stack trace of the error
|
||||
- [ ] Confirm reproduction steps
|
||||
- [ ] Understand expected behavior
|
||||
|
||||
### During Debugging
|
||||
- [ ] Check recent code changes
|
||||
- [ ] Use binary search to locate the issue
|
||||
- [ ] Add logs to trace variables
|
||||
- [ ] Verify hypotheses
|
||||
|
||||
### After Resolution
|
||||
- [ ] Confirm the original error is fixed
|
||||
- [ ] Test related functionality
|
||||
- [ ] Add tests to prevent regression
|
||||
- [ ] Document the problem and solution
|
||||
|
||||
## Additional Resources
|
||||
|
||||
### Reference Files
|
||||
|
||||
For detailed debugging techniques and patterns:
|
||||
- **`references/python-errors.md`** - Python error details
|
||||
- **`references/javascript-errors.md`** - JavaScript/TypeScript error details
|
||||
- **`references/shell-errors.md`** - Bash/Zsh script error details
|
||||
- **`references/debugging-tools.md`** - Debugging tools usage guide
|
||||
- **`references/common-patterns.md`** - Common error patterns
|
||||
|
||||
### Example Files
|
||||
|
||||
Working debugging examples:
|
||||
- **`examples/debugging-workflow.py`** - Complete debugging workflow example
|
||||
- **`examples/error-handling-patterns.py`** - Error handling patterns
|
||||
- **`examples/debugging-workflow.sh`** - Shell script debugging example
|
||||
@@ -0,0 +1,241 @@
|
||||
"""
|
||||
调试流程示例
|
||||
|
||||
这个示例展示了完整的调试流程,从发现问题到解决问题。
|
||||
"""
|
||||
|
||||
import logging
|
||||
|
||||
# 配置日志
|
||||
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s')
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# ============================================
|
||||
# 问题 1:IndexError
|
||||
# ============================================
|
||||
|
||||
def get_item(items, index):
|
||||
"""
|
||||
问题:直接访问索引可能导致 IndexError
|
||||
|
||||
错误现象:
|
||||
IndexError: list index out of range
|
||||
"""
|
||||
# ❌ 有问题的代码
|
||||
# return items[index]
|
||||
|
||||
# ✅ 修复后的代码
|
||||
if 0 <= index < len(items):
|
||||
return items[index]
|
||||
else:
|
||||
logger.warning(f"索引 {index} 超出范围 [0, {len(items)})")
|
||||
return None
|
||||
|
||||
|
||||
# ============================================
|
||||
# 问题 2:TypeError - 字符串拼接
|
||||
# ============================================
|
||||
|
||||
def format_message(name, count):
|
||||
"""
|
||||
问题:尝试拼接字符串和数字
|
||||
|
||||
错误现象:
|
||||
TypeError: can only concatenate str (not "int") to str
|
||||
"""
|
||||
# ❌ 有问题的代码
|
||||
# return name + ": " + count
|
||||
|
||||
# ✅ 修复后的代码
|
||||
return f"{name}: {count}"
|
||||
# 或
|
||||
# return name + ": " + str(count)
|
||||
|
||||
|
||||
# ============================================
|
||||
# 问题 3:KeyError
|
||||
# ============================================
|
||||
|
||||
def get_user_info(users, user_id):
|
||||
"""
|
||||
问题:直接访问字典中可能不存在的键
|
||||
|
||||
错误现象:
|
||||
KeyError: 'user_123'
|
||||
"""
|
||||
# ❌ 有问题的代码
|
||||
# return users[user_id]
|
||||
|
||||
# ✅ 修复后的代码 - 方法1:使用 get()
|
||||
return users.get(user_id, None)
|
||||
|
||||
# ✅ 修复后的代码 - 方法2:检查键是否存在
|
||||
# if user_id in users:
|
||||
# return users[user_id]
|
||||
# return None
|
||||
|
||||
|
||||
# ============================================
|
||||
# 问题 4:AttributeError - None 对象
|
||||
# ============================================
|
||||
|
||||
def process_data(data_provider):
|
||||
"""
|
||||
问题:data_provider 可能返回 None
|
||||
|
||||
错误现象:
|
||||
AttributeError: 'NoneType' object has no attribute 'process'
|
||||
"""
|
||||
data = data_provider.get_data()
|
||||
|
||||
# ❌ 有问题的代码
|
||||
# return data.process()
|
||||
|
||||
# ✅ 修复后的代码
|
||||
if data is not None:
|
||||
return data.process()
|
||||
else:
|
||||
logger.error("数据为 None,无法处理")
|
||||
return None
|
||||
|
||||
|
||||
# ============================================
|
||||
# 问题 5:修改正在迭代的列表
|
||||
# ============================================
|
||||
|
||||
def remove_even_numbers(numbers):
|
||||
"""
|
||||
问题:迭代时修改列表导致跳过元素
|
||||
|
||||
错误现象:
|
||||
某些偶数没有被移除
|
||||
"""
|
||||
# ❌ 有问题的代码
|
||||
# for num in numbers:
|
||||
# if num % 2 == 0:
|
||||
# numbers.remove(num)
|
||||
# return numbers
|
||||
|
||||
# ✅ 修复后的代码 - 方法1:列表推导式
|
||||
return [num for num in numbers if num % 2 != 0]
|
||||
|
||||
# ✅ 修复后的代码 - 方法2:使用副本
|
||||
# for num in numbers[:]:
|
||||
# if num % 2 == 0:
|
||||
# numbers.remove(num)
|
||||
# return numbers
|
||||
|
||||
|
||||
# ============================================
|
||||
# 调试技巧示例
|
||||
# ============================================
|
||||
|
||||
def debug_with_logging(data):
|
||||
"""
|
||||
使用日志追踪问题
|
||||
"""
|
||||
logger.debug(f"输入数据: {data}")
|
||||
|
||||
# 步骤 1
|
||||
processed = step1(data)
|
||||
logger.debug(f"步骤1结果: {processed}")
|
||||
|
||||
# 步骤 2
|
||||
result = step2(processed)
|
||||
logger.debug(f"步骤2结果: {result}")
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def step1(data):
|
||||
"""模拟步骤1"""
|
||||
return [x * 2 for x in data]
|
||||
|
||||
|
||||
def step2(data):
|
||||
"""模拟步骤2"""
|
||||
return sum(data)
|
||||
|
||||
|
||||
# ============================================
|
||||
# 异常处理示例
|
||||
# ============================================
|
||||
|
||||
def safe_divide(a, b):
|
||||
"""
|
||||
正确的异常处理模式
|
||||
"""
|
||||
try:
|
||||
result = a / b
|
||||
logger.info(f"{a} / {b} = {result}")
|
||||
return result
|
||||
except ZeroDivisionError:
|
||||
logger.error(f"除数不能为零: {b}")
|
||||
return None
|
||||
except TypeError as e:
|
||||
logger.error(f"类型错误: {e}")
|
||||
return None
|
||||
|
||||
|
||||
# ============================================
|
||||
# 使用断言进行调试
|
||||
# ============================================
|
||||
|
||||
def calculate_discount(price, discount_rate):
|
||||
"""
|
||||
使用断言验证假设
|
||||
"""
|
||||
# 断言:价格应该是正数
|
||||
assert price > 0, f"价格应该是正数,实际: {price}"
|
||||
|
||||
# 断言:折扣率应该在 0-1 之间
|
||||
assert 0 <= discount_rate <= 1, f"折扣率应该在 0-1 之间,实际: {discount_rate}"
|
||||
|
||||
discounted_price = price * (1 - discount_rate)
|
||||
|
||||
# 断言:折扣后的价格应该小于原价
|
||||
assert discounted_price <= price, "折扣价格应该小于原价"
|
||||
|
||||
return discounted_price
|
||||
|
||||
|
||||
# ============================================
|
||||
# 测试代码
|
||||
# ============================================
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("=" * 50)
|
||||
print("调试示例")
|
||||
print("=" * 50)
|
||||
|
||||
# 测试 get_item
|
||||
print("\n1. 测试 get_item:")
|
||||
items = ['a', 'b', 'c']
|
||||
print(f"get_item(items, 1) = {get_item(items, 1)}")
|
||||
print(f"get_item(items, 10) = {get_item(items, 10)}")
|
||||
|
||||
# 测试 format_message
|
||||
print("\n2. 测试 format_message:")
|
||||
print(f"format_message('Count', 42) = {format_message('Count', 42)}")
|
||||
|
||||
# 测试 get_user_info
|
||||
print("\n3. 测试 get_user_info:")
|
||||
users = {'user_1': 'Alice', 'user_2': 'Bob'}
|
||||
print(f"get_user_info(users, 'user_1') = {get_user_info(users, 'user_1')}")
|
||||
print(f"get_user_info(users, 'user_999') = {get_user_info(users, 'user_999')}")
|
||||
|
||||
# 测试 remove_even_numbers
|
||||
print("\n4. 测试 remove_even_numbers:")
|
||||
numbers = [1, 2, 3, 4, 5, 6]
|
||||
print(f"原始: {numbers}")
|
||||
print(f"结果: {remove_even_numbers(numbers)}")
|
||||
|
||||
# 测试 safe_divide
|
||||
print("\n5. 测试 safe_divide:")
|
||||
print(f"safe_divide(10, 2) = {safe_divide(10, 2)}")
|
||||
print(f"safe_divide(10, 0) = {safe_divide(10, 0)}")
|
||||
|
||||
# 测试 calculate_discount
|
||||
print("\n6. 测试 calculate_discount:")
|
||||
print(f"calculate_discount(100, 0.2) = {calculate_discount(100, 0.2)}")
|
||||
400
文档润色流和知识库构建流/claude-scholar/skills/bug-detective/examples/debugging-workflow.sh
Executable file
400
文档润色流和知识库构建流/claude-scholar/skills/bug-detective/examples/debugging-workflow.sh
Executable file
@@ -0,0 +1,400 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Shell 脚本调试流程示例
|
||||
# 展示常见的 Bash 脚本错误和调试方法
|
||||
#
|
||||
|
||||
# ============================================
|
||||
# 调试设置
|
||||
# ============================================
|
||||
|
||||
# 取消注释以启用调试模式
|
||||
# set -x # 打印每个命令
|
||||
# set -e # 错误时退出
|
||||
# set -u # 未定义变量报错
|
||||
# set -o pipefail # 管道中任何命令失败则失败
|
||||
|
||||
# 或组合使用
|
||||
# set -xeuo pipefail # 严格模式
|
||||
|
||||
|
||||
# ============================================
|
||||
# 错误处理函数
|
||||
# ============================================
|
||||
|
||||
# 错误退出函数
|
||||
die() {
|
||||
local message="$1"
|
||||
local exit_code="${2:-1}"
|
||||
echo "Error: $message" >&2
|
||||
exit "$exit_code"
|
||||
}
|
||||
|
||||
# 记录函数
|
||||
log() {
|
||||
local level="$1"
|
||||
shift
|
||||
echo "[$(date '+%Y-%m-%d %H:%M:%S')] [$level] $*" >&2
|
||||
}
|
||||
|
||||
log_info() { log "INFO" "$@"; }
|
||||
log_warn() { log "WARN" "$@"; }
|
||||
log_error() { log "ERROR" "$@"; }
|
||||
|
||||
|
||||
# ============================================
|
||||
# 问题 1:未引用的变量
|
||||
# ============================================
|
||||
|
||||
# 错误示例:变量未引用
|
||||
demo_unquoted_variable() {
|
||||
echo "=== 问题 1:未引用的变量 ==="
|
||||
|
||||
local name="John Doe"
|
||||
|
||||
# 错误:变量未引用,空值会导致语法错误
|
||||
# if [ $name = "John Doe" ]; then
|
||||
# echo "Match"
|
||||
# fi
|
||||
|
||||
# 正确:始终引用变量
|
||||
if [ "$name" = "John Doe" ]; then
|
||||
log_info "变量匹配: $name"
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
# ============================================
|
||||
# 问题 2:命令失败继续执行
|
||||
# ============================================
|
||||
|
||||
demo_command_failure() {
|
||||
echo "=== 问题 2:命令失败继续执行 ==="
|
||||
|
||||
# 错误:cd 失败后继续执行
|
||||
# cd /nonexistent_directory
|
||||
# rm -rf file.txt # 会删除当前目录的文件!
|
||||
|
||||
# 正确:检查命令是否成功
|
||||
cd /tmp || die "无法切换到 /tmp 目录"
|
||||
log_info "成功切换到目录: $(pwd)"
|
||||
|
||||
cd - > /dev/null || true
|
||||
}
|
||||
|
||||
|
||||
# ============================================
|
||||
# 问题 3:循环中的变量作用域(管道问题)
|
||||
# ============================================
|
||||
|
||||
demo_pipeline_scope() {
|
||||
echo "=== 问题 3:管道中的变量作用域 ==="
|
||||
|
||||
local count=0
|
||||
|
||||
# 错误:管道创建子 shell,外部变量不改变
|
||||
# echo -e "1\n2\n3" | while read line; do
|
||||
# count=$((count + 1))
|
||||
# done
|
||||
# echo "Count: $count" # 输出 0
|
||||
|
||||
# 正确:使用重定向
|
||||
while read line; do
|
||||
count=$((count + 1))
|
||||
done < <(echo -e "1\n2\n3")
|
||||
log_info "计数结果: $count"
|
||||
}
|
||||
|
||||
|
||||
# ============================================
|
||||
# 问题 4:数组操作
|
||||
# ============================================
|
||||
|
||||
demo_array_operations() {
|
||||
echo "=== 问题 4:数组操作 ==="
|
||||
|
||||
local fruits=("apple" "banana" "cherry")
|
||||
|
||||
# 错误的数组访问
|
||||
# echo $fruits[1] # 输出 apple[1]
|
||||
|
||||
# 正确的数组访问
|
||||
log_info "第一个元素: ${fruits[0]}"
|
||||
log_info "第二个元素: ${fruits[1]}"
|
||||
log_info "所有元素: ${fruits[@]}"
|
||||
log_info "数组长度: ${#fruits[@]}"
|
||||
|
||||
# 遍历数组
|
||||
for fruit in "${fruits[@]}"; do
|
||||
log_info "水果: $fruit"
|
||||
done
|
||||
}
|
||||
|
||||
|
||||
# ============================================
|
||||
# 问题 5:字符串比较
|
||||
# ============================================
|
||||
|
||||
demo_string_comparison() {
|
||||
echo "=== 问题 5:字符串比较 ==="
|
||||
|
||||
local name="John"
|
||||
|
||||
# 错误:数字比较使用 =
|
||||
# if [ $age = 18 ]; then
|
||||
|
||||
# 正确:字符串比较
|
||||
if [[ "$name" == "John" ]]; then
|
||||
log_info "字符串匹配"
|
||||
fi
|
||||
|
||||
# 正确:数字比较
|
||||
local age=18
|
||||
if [ "$age" -eq 18 ]; then
|
||||
log_info "数字匹配"
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
# ============================================
|
||||
# 问题 6:算术运算
|
||||
# ============================================
|
||||
|
||||
demo_arithmetic() {
|
||||
echo "=== 问题 6:算术运算 ==="
|
||||
|
||||
local a=10
|
||||
local b=5
|
||||
|
||||
# 错误:使用 let 或 $(())
|
||||
# result = a + b # 这是命令调用
|
||||
|
||||
# 正确的算术运算
|
||||
local result=$((a + b))
|
||||
log_info "加法: $a + $b = $result"
|
||||
|
||||
result=$((a - b))
|
||||
log_info "减法: $a - $b = $result"
|
||||
|
||||
result=$((a * b))
|
||||
log_info "乘法: $a * $b = $result"
|
||||
|
||||
result=$((a / b))
|
||||
log_info "除法: $a / $b = $result"
|
||||
|
||||
# 使用 let
|
||||
let result=a+b
|
||||
log_info "let 加法: $result"
|
||||
}
|
||||
|
||||
|
||||
# ============================================
|
||||
# 参数验证
|
||||
# ============================================
|
||||
|
||||
validate_arguments() {
|
||||
echo "=== 参数验证 ==="
|
||||
|
||||
# 检查参数数量
|
||||
if [ $# -lt 2 ]; then
|
||||
die "用法: $0 <文件> <目录>" 2
|
||||
fi
|
||||
|
||||
local file="$1"
|
||||
local dir="$2"
|
||||
|
||||
# 检查文件存在
|
||||
if [ ! -f "$file" ]; then
|
||||
die "文件不存在: $file" 3
|
||||
fi
|
||||
|
||||
# 检查目录存在
|
||||
if [ ! -d "$dir" ]; then
|
||||
die "目录不存在: $dir" 4
|
||||
fi
|
||||
|
||||
log_info "参数验证通过"
|
||||
log_info "文件: $file"
|
||||
log_info "目录: $dir"
|
||||
}
|
||||
|
||||
|
||||
# ============================================
|
||||
# 使用 trap 进行清理
|
||||
# ============================================
|
||||
|
||||
demo_trap() {
|
||||
echo "=== 使用 trap 进行清理 ==="
|
||||
|
||||
# 设置清理函数
|
||||
cleanup() {
|
||||
log_info "执行清理操作..."
|
||||
# 这里可以执行清理操作
|
||||
}
|
||||
|
||||
# 捕获退出信号
|
||||
trap cleanup EXIT
|
||||
|
||||
# 捕获错误信号
|
||||
trap 'log_error "发生错误,行号: $LINENO"' ERR
|
||||
|
||||
# 捕获中断信号
|
||||
trap 'log_warn "脚本被中断"; cleanup; exit 130' INT
|
||||
|
||||
log_info "执行一些操作..."
|
||||
# 模拟操作
|
||||
sleep 1
|
||||
}
|
||||
|
||||
|
||||
# ============================================
|
||||
# 调试技巧示例
|
||||
# ============================================
|
||||
|
||||
demo_debugging() {
|
||||
echo "=== 调试技巧 ==="
|
||||
|
||||
# 1. 使用 echo 调试
|
||||
local value="test"
|
||||
echo "[DEBUG] value = $value" >&2
|
||||
|
||||
# 2. 使用 printf 格式化输出
|
||||
printf "[DEBUG] Count: %d, Name: %s\n" 42 "John" >&2
|
||||
|
||||
# 3. 检查变量是否设置
|
||||
if [ -z "${unset_var+x}" ]; then
|
||||
log_warn "变量 unset_var 未设置"
|
||||
fi
|
||||
|
||||
# 4. 显示调用栈
|
||||
log_info "调用栈:"
|
||||
local i=0
|
||||
while caller $i; do
|
||||
((i++))
|
||||
done 2>/dev/null || true
|
||||
}
|
||||
|
||||
|
||||
# ============================================
|
||||
# 文件操作错误处理
|
||||
# ============================================
|
||||
|
||||
demo_file_operations() {
|
||||
echo "=== 文件操作错误处理 ==="
|
||||
|
||||
local tmpfile=$(mktemp) || die "无法创建临时文件"
|
||||
|
||||
# 确保文件被删除
|
||||
trap "rm -f '$tmpfile'" EXIT
|
||||
|
||||
# 写入文件
|
||||
echo "Test content" > "$tmpfile" || die "无法写入文件: $tmpfile"
|
||||
|
||||
# 读取文件
|
||||
local content
|
||||
content=$(cat "$tmpfile") || die "无法读取文件: $tmpfile"
|
||||
|
||||
log_info "文件内容: $content"
|
||||
|
||||
# trap 会自动清理
|
||||
}
|
||||
|
||||
|
||||
# ============================================
|
||||
# 带重试的操作
|
||||
# ============================================
|
||||
|
||||
demo_retry() {
|
||||
echo "=== 带重试的操作 ==="
|
||||
|
||||
local max_attempts=3
|
||||
local attempt=1
|
||||
|
||||
while [ $attempt -le $max_attempts ]; do
|
||||
log_info "尝试 $attempt/$max_attempts..."
|
||||
|
||||
# 模拟可能失败的操作
|
||||
if [ $attempt -eq 2 ]; then
|
||||
log_info "成功!"
|
||||
return 0
|
||||
fi
|
||||
|
||||
log_warn "失败,重试..."
|
||||
((attempt++))
|
||||
sleep 1
|
||||
done
|
||||
|
||||
log_error "所有尝试都失败了"
|
||||
return 1
|
||||
}
|
||||
|
||||
|
||||
# ============================================
|
||||
# 检查依赖
|
||||
# ============================================
|
||||
|
||||
check_dependencies() {
|
||||
echo "=== 检查依赖 ==="
|
||||
|
||||
local required_commands=("curl" "jq" "git")
|
||||
|
||||
for cmd in "${required_commands[@]}"; do
|
||||
if ! command -v "$cmd" &> /dev/null; then
|
||||
log_error "缺少依赖: $cmd"
|
||||
return 1
|
||||
fi
|
||||
log_info "✓ $cmd 可用"
|
||||
done
|
||||
|
||||
log_info "所有依赖都已满足"
|
||||
}
|
||||
|
||||
|
||||
# ============================================
|
||||
# 主函数
|
||||
# ============================================
|
||||
|
||||
main() {
|
||||
echo "============================================"
|
||||
echo "Shell 脚本调试示例"
|
||||
echo "============================================"
|
||||
|
||||
# 运行各个示例
|
||||
demo_unquoted_variable
|
||||
echo ""
|
||||
|
||||
demo_command_failure
|
||||
echo ""
|
||||
|
||||
demo_pipeline_scope
|
||||
echo ""
|
||||
|
||||
demo_array_operations
|
||||
echo ""
|
||||
|
||||
demo_string_comparison
|
||||
echo ""
|
||||
|
||||
demo_arithmetic
|
||||
echo ""
|
||||
|
||||
demo_trap
|
||||
echo ""
|
||||
|
||||
demo_debugging
|
||||
echo ""
|
||||
|
||||
demo_file_operations
|
||||
echo ""
|
||||
|
||||
demo_retry
|
||||
echo ""
|
||||
|
||||
check_dependencies
|
||||
echo ""
|
||||
|
||||
log_info "所有示例执行完成"
|
||||
}
|
||||
|
||||
# 运行主函数
|
||||
main "$@"
|
||||
@@ -0,0 +1,305 @@
|
||||
"""
|
||||
错误处理模式示例
|
||||
|
||||
展示各种错误处理的最佳实践
|
||||
"""
|
||||
|
||||
import logging
|
||||
from typing import Optional, List, Dict, Any
|
||||
from functools import wraps
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# ============================================
|
||||
# 模式 1:具体异常捕获
|
||||
# ============================================
|
||||
|
||||
def read_file(filepath: str) -> Optional[str]:
|
||||
"""
|
||||
捕获具体异常而不是宽泛的 Exception
|
||||
"""
|
||||
try:
|
||||
with open(filepath, 'r') as f:
|
||||
return f.read()
|
||||
except FileNotFoundError:
|
||||
logger.error(f"文件不存在: {filepath}")
|
||||
return None
|
||||
except PermissionError:
|
||||
logger.error(f"没有权限读取文件: {filepath}")
|
||||
return None
|
||||
except UnicodeDecodeError:
|
||||
logger.error(f"文件编码错误: {filepath}")
|
||||
return None
|
||||
|
||||
|
||||
# ============================================
|
||||
# 模式 2:带重试的操作
|
||||
# ============================================
|
||||
|
||||
def retry_operation(max_attempts: int = 3):
|
||||
"""
|
||||
装饰器:自动重试失败的操作
|
||||
"""
|
||||
def decorator(func):
|
||||
@wraps(func)
|
||||
def wrapper(*args, **kwargs):
|
||||
for attempt in range(1, max_attempts + 1):
|
||||
try:
|
||||
return func(*args, **kwargs)
|
||||
except Exception as e:
|
||||
if attempt == max_attempts:
|
||||
logger.error(f"操作失败,已重试 {max_attempts} 次: {e}")
|
||||
raise
|
||||
logger.warning(f"操作失败,第 {attempt} 次重试...")
|
||||
return None
|
||||
return wrapper
|
||||
return decorator
|
||||
|
||||
|
||||
@retry_operation(max_attempts=3)
|
||||
def unstable_api_call() -> Dict[str, Any]:
|
||||
"""
|
||||
模拟不稳定的 API 调用
|
||||
"""
|
||||
import random
|
||||
if random.random() < 0.7: # 70% 失败率
|
||||
raise ConnectionError("API 连接失败")
|
||||
return {"status": "success", "data": "result"}
|
||||
|
||||
|
||||
# ============================================
|
||||
# 模式 3:上下文管理器处理资源
|
||||
# ============================================
|
||||
|
||||
class DatabaseConnection:
|
||||
"""
|
||||
自定义上下文管理器,确保资源正确释放
|
||||
"""
|
||||
def __init__(self, connection_string: str):
|
||||
self.connection_string = connection_string
|
||||
self.connection = None
|
||||
|
||||
def __enter__(self):
|
||||
logger.info(f"连接数据库: {self.connection_string}")
|
||||
self.connection = f"Connection to {self.connection_string}"
|
||||
return self.connection
|
||||
|
||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||
if exc_type is not None:
|
||||
logger.error(f"发生异常: {exc_val}")
|
||||
logger.info("关闭数据库连接")
|
||||
# 清理资源
|
||||
self.connection = None
|
||||
return False # 不抑制异常
|
||||
|
||||
|
||||
# ============================================
|
||||
# 模式 4:链式异常(保留原始异常)
|
||||
# ============================================
|
||||
|
||||
def validate_and_process(data: Dict[str, Any]) -> Any:
|
||||
"""
|
||||
使用 raise from 保留原始异常链
|
||||
"""
|
||||
try:
|
||||
# 验证数据
|
||||
if 'value' not in data:
|
||||
raise ValueError("数据中缺少 'value' 字段")
|
||||
|
||||
value = data['value']
|
||||
if not isinstance(value, (int, float)):
|
||||
raise TypeError(f"value 应该是数字,实际类型: {type(value)}")
|
||||
|
||||
# 处理数据
|
||||
return value * 2
|
||||
|
||||
except (ValueError, TypeError) as e:
|
||||
# 保留原始异常并添加上下文
|
||||
raise RuntimeError(f"数据处理失败: {data}") from e
|
||||
|
||||
|
||||
# ============================================
|
||||
# 模式 5:结果对象模式(不使用异常)
|
||||
# ============================================
|
||||
|
||||
class Result:
|
||||
"""
|
||||
结果对象模式:封装成功/失败状态
|
||||
"""
|
||||
def __init__(self, success: bool, value: Any = None, error: str = None):
|
||||
self.success = success
|
||||
self.value = value
|
||||
self.error = error
|
||||
|
||||
@classmethod
|
||||
def ok(cls, value: Any) -> 'Result':
|
||||
return cls(success=True, value=value)
|
||||
|
||||
@classmethod
|
||||
def err(cls, error: str) -> 'Result':
|
||||
return cls(success=False, error=error)
|
||||
|
||||
def is_ok(self) -> bool:
|
||||
return self.success
|
||||
|
||||
def is_err(self) -> bool:
|
||||
return not self.success
|
||||
|
||||
def unwrap(self) -> Any:
|
||||
if not self.success:
|
||||
raise ValueError(f"尝试解包错误结果: {self.error}")
|
||||
return self.value
|
||||
|
||||
def unwrap_or(self, default: Any) -> Any:
|
||||
return self.value if self.success else default
|
||||
|
||||
|
||||
def safe_divide_result(a: float, b: float) -> Result:
|
||||
"""
|
||||
使用结果对象而不是异常
|
||||
"""
|
||||
if b == 0:
|
||||
return Result.err(f"除数不能为零: {b}")
|
||||
|
||||
try:
|
||||
return Result.ok(a / b)
|
||||
except Exception as e:
|
||||
return Result.err(f"计算失败: {e}")
|
||||
|
||||
|
||||
# ============================================
|
||||
# 模式 6:多项验证错误收集
|
||||
# ============================================
|
||||
|
||||
class ValidationError(Exception):
|
||||
"""自定义验证错误"""
|
||||
def __init__(self, errors: List[str]):
|
||||
self.errors = errors
|
||||
super().__init__("\n".join(errors))
|
||||
|
||||
|
||||
def validate_user(data: Dict[str, Any]) -> None:
|
||||
"""
|
||||
收集所有验证错误而不是遇到第一个就停止
|
||||
"""
|
||||
errors = []
|
||||
|
||||
if 'name' not in data:
|
||||
errors.append("缺少 'name' 字段")
|
||||
elif not isinstance(data['name'], str):
|
||||
errors.append("'name' 应该是字符串")
|
||||
elif len(data['name']) < 2:
|
||||
errors.append("'name' 长度应该至少为 2")
|
||||
|
||||
if 'age' not in data:
|
||||
errors.append("缺少 'age' 字段")
|
||||
elif not isinstance(data['age'], int):
|
||||
errors.append("'age' 应该是整数")
|
||||
elif data['age'] < 0 or data['age'] > 150:
|
||||
errors.append("'age' 应该在 0-150 之间")
|
||||
|
||||
if 'email' in data and '@' not in data['email']:
|
||||
errors.append("'email' 格式不正确")
|
||||
|
||||
if errors:
|
||||
raise ValidationError(errors)
|
||||
|
||||
|
||||
# ============================================
|
||||
# 模式 7:默认值和回退
|
||||
# ============================================
|
||||
|
||||
def get_config(config: Dict[str, Any], key: str, default: Any = None) -> Any:
|
||||
"""
|
||||
安全获取配置,支持多级键和默认值
|
||||
"""
|
||||
if '.' in key:
|
||||
# 支持嵌套键,如 "database.host"
|
||||
keys = key.split('.')
|
||||
value = config
|
||||
for k in keys:
|
||||
if isinstance(value, dict) and k in value:
|
||||
value = value[k]
|
||||
else:
|
||||
return default
|
||||
return value
|
||||
|
||||
# 简单键
|
||||
return config.get(key, default)
|
||||
|
||||
|
||||
# ============================================
|
||||
# 模式 8:优雅降级
|
||||
# ============================================
|
||||
|
||||
def get_user_preferences(user_id: int) -> Dict[str, Any]:
|
||||
"""
|
||||
尝试多种方法获取用户偏好,优雅降级
|
||||
"""
|
||||
# 尝试 1:从缓存获取
|
||||
try:
|
||||
return _get_from_cache(user_id)
|
||||
except Exception as e:
|
||||
logger.warning(f"从缓存获取失败: {e}")
|
||||
|
||||
# 尝试 2:从数据库获取
|
||||
try:
|
||||
return _get_from_database(user_id)
|
||||
except Exception as e:
|
||||
logger.warning(f"从数据库获取失败: {e}")
|
||||
|
||||
# 尝试 3:使用默认配置
|
||||
logger.info("使用默认配置")
|
||||
return _get_default_preferences()
|
||||
|
||||
|
||||
def _get_from_cache(user_id: int) -> Dict[str, Any]:
|
||||
# 模拟缓存失败
|
||||
raise ConnectionError("缓存连接失败")
|
||||
|
||||
|
||||
def _get_from_database(user_id: int) -> Dict[str, Any]:
|
||||
# 模拟数据库失败
|
||||
raise ConnectionError("数据库连接失败")
|
||||
|
||||
|
||||
def _get_default_preferences() -> Dict[str, Any]:
|
||||
return {
|
||||
"theme": "light",
|
||||
"language": "zh-CN",
|
||||
"notifications": True
|
||||
}
|
||||
|
||||
|
||||
# ============================================
|
||||
# 测试代码
|
||||
# ============================================
|
||||
|
||||
if __name__ == "__main__":
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
print("=" * 50)
|
||||
print("错误处理模式示例")
|
||||
print("=" * 50)
|
||||
|
||||
# 测试结果对象模式
|
||||
print("\n1. 结果对象模式:")
|
||||
result1 = safe_divide_result(10, 2)
|
||||
print(f"10 / 2 = {result1.unwrap()}")
|
||||
|
||||
result2 = safe_divide_result(10, 0)
|
||||
print(f"10 / 0 = {result2.unwrap_or('N/A')} ({result2.error})")
|
||||
|
||||
# 测试优雅降级
|
||||
print("\n2. 优雅降级:")
|
||||
prefs = get_user_preferences(123)
|
||||
print(f"用户偏好: {prefs}")
|
||||
|
||||
# 测试上下文管理器
|
||||
print("\n3. 上下文管理器:")
|
||||
try:
|
||||
with DatabaseConnection("localhost:5432") as conn:
|
||||
print(f"连接: {conn}")
|
||||
except Exception as e:
|
||||
print(f"操作失败: {e}")
|
||||
@@ -0,0 +1,308 @@
|
||||
# 常见错误模式
|
||||
|
||||
## 通用编程错误模式
|
||||
|
||||
### 1. Off-by-one Error(差一错误)
|
||||
|
||||
**描述**:循环或索引中出现 ±1 的偏差
|
||||
|
||||
**示例**:
|
||||
```python
|
||||
# ❌ 错误:范围应该是 range(n) 而不是 range(n+1)
|
||||
for i in range(len(items) + 1):
|
||||
print(items[i]) # IndexError
|
||||
|
||||
# ✅ 正确
|
||||
for i in range(len(items)):
|
||||
print(items[i])
|
||||
```
|
||||
|
||||
### 2. Null/None 引用错误
|
||||
|
||||
**描述**:尝试访问 None 对象的属性或方法
|
||||
|
||||
**Python**:
|
||||
```python
|
||||
# ❌ 可能返回 None
|
||||
result = get_data()
|
||||
print(result.value) # AttributeError
|
||||
|
||||
# ✅ 检查 None
|
||||
result = get_data()
|
||||
if result is not None:
|
||||
print(result.value)
|
||||
```
|
||||
|
||||
**JavaScript**:
|
||||
```javascript
|
||||
// ❌ 可能是 null
|
||||
const user = getUser();
|
||||
console.log(user.name); // TypeError
|
||||
|
||||
// ✅ 使用可选链
|
||||
console.log(user?.name);
|
||||
```
|
||||
|
||||
### 3. 资源泄漏
|
||||
|
||||
**描述**:打开的资源(文件、连接)未正确关闭
|
||||
|
||||
**Python**:
|
||||
```python
|
||||
# ❌ 文件可能未关闭
|
||||
f = open("file.txt")
|
||||
content = f.read()
|
||||
# 如果发生异常,文件不会关闭
|
||||
|
||||
# ✅ 使用 with 语句
|
||||
with open("file.txt") as f:
|
||||
content = f.read()
|
||||
# 文件自动关闭
|
||||
```
|
||||
|
||||
### 4. 竞态条件
|
||||
|
||||
**描述**:多线程/进程间的时序依赖问题
|
||||
|
||||
**示例**:
|
||||
```python
|
||||
# ❌ 检查后使用(TOCTOU)
|
||||
if os.path.exists("file.txt"):
|
||||
# 其他进程可能在这之间删除文件
|
||||
with open("file.txt") as f:
|
||||
content = f.read()
|
||||
|
||||
# ✅ 直接尝试并处理异常
|
||||
try:
|
||||
with open("file.txt") as f:
|
||||
content = f.read()
|
||||
except FileNotFoundError:
|
||||
content = None
|
||||
```
|
||||
|
||||
### 5. 忘记返回值
|
||||
|
||||
**描述**:函数没有显式返回值,导致返回 None
|
||||
|
||||
**示例**:
|
||||
```python
|
||||
# ❌ 忘记返回结果
|
||||
def calculate(x, y):
|
||||
result = x + y
|
||||
# 忘记 return
|
||||
|
||||
# ✅ 正确返回
|
||||
def calculate(x, y):
|
||||
return x + y
|
||||
```
|
||||
|
||||
### 6. 错误的比较运算符
|
||||
|
||||
**描述**:使用 = 代替 ==,或混淆 is 和 ==
|
||||
|
||||
**Python**:
|
||||
```python
|
||||
# ❌ 赋值而不是比较
|
||||
if x = 5: # SyntaxError
|
||||
|
||||
# ❌ 使用 is 比较值
|
||||
if x is 5: # 不保证正确
|
||||
|
||||
# ✅ 正确
|
||||
if x == 5:
|
||||
```
|
||||
|
||||
### 7. 浮点数精度问题
|
||||
|
||||
**描述**:浮点数比较因精度问题失败
|
||||
|
||||
**示例**:
|
||||
```python
|
||||
# ❌ 直接比较浮点数
|
||||
if 0.1 + 0.2 == 0.3: # False
|
||||
print("相等")
|
||||
|
||||
# ✅ 使用容差比较
|
||||
if abs((0.1 + 0.2) - 0.3) < 1e-9:
|
||||
print("相等")
|
||||
|
||||
# 或使用 math.isclose()
|
||||
import math
|
||||
if math.isclose(0.1 + 0.2, 0.3):
|
||||
print("相等")
|
||||
```
|
||||
|
||||
### 8. 字符串拼接性能问题
|
||||
|
||||
**描述**:在循环中使用 + 拼接字符串
|
||||
|
||||
**示例**:
|
||||
```python
|
||||
# ❌ 低效:每次创建新字符串
|
||||
result = ""
|
||||
for item in items:
|
||||
result += str(item)
|
||||
|
||||
# ✅ 高效:使用列表和 join
|
||||
result = "".join(str(item) for item in items)
|
||||
```
|
||||
|
||||
## Python 特有模式
|
||||
|
||||
### 1. 可变默认参数
|
||||
|
||||
```python
|
||||
# ❌ 所有调用共享同一个列表
|
||||
def append(item, items=[]):
|
||||
items.append(item)
|
||||
return items
|
||||
|
||||
# ✅ 使用 None 作为默认值
|
||||
def append(item, items=None):
|
||||
if items is None:
|
||||
items = []
|
||||
items.append(item)
|
||||
return items
|
||||
```
|
||||
|
||||
### 2. 闭包变量绑定问题
|
||||
|
||||
```python
|
||||
# ❌ 所有函数使用相同的 i 值
|
||||
funcs = [lambda: i for i in range(3)]
|
||||
# 所有函数都返回 2
|
||||
|
||||
# ✅ 使用默认参数捕获值
|
||||
funcs = [lambda i=i: i for i in range(3)]
|
||||
```
|
||||
|
||||
### 3. 修改正在迭代的序列
|
||||
|
||||
```python
|
||||
# ❌ 迭代时修改列表
|
||||
items = [1, 2, 3, 4]
|
||||
for item in items:
|
||||
if item % 2 == 0:
|
||||
items.remove(item)
|
||||
|
||||
# ✅ 创建新列表或使用副本
|
||||
items = [item for item in items if item % 2 != 0]
|
||||
|
||||
# 或
|
||||
for item in items[:]:
|
||||
if item % 2 == 0:
|
||||
items.remove(item)
|
||||
```
|
||||
|
||||
## JavaScript/TypeScript 特有模式
|
||||
|
||||
### 1. this 绑定问题
|
||||
|
||||
```javascript
|
||||
// ❌ this 丢失上下文
|
||||
class Counter {
|
||||
count = 0;
|
||||
increment() {
|
||||
setTimeout(function() {
|
||||
this.count++; // this 不是 Counter 实例
|
||||
}, 100);
|
||||
}
|
||||
}
|
||||
|
||||
// ✅ 使用箭头函数
|
||||
class Counter {
|
||||
count = 0;
|
||||
increment() {
|
||||
setTimeout(() => {
|
||||
this.count++; // this 正确绑定
|
||||
}, 100);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 异步错误处理
|
||||
|
||||
```javascript
|
||||
// ❌ 没有处理 Promise 错误
|
||||
async function getData() {
|
||||
const response = await fetch(url);
|
||||
return response.json(); // 如果失败会抛出异常
|
||||
}
|
||||
|
||||
// ✅ 使用 try-catch
|
||||
async function getData() {
|
||||
try {
|
||||
const response = await fetch(url);
|
||||
return await response.json();
|
||||
} catch (error) {
|
||||
console.error("获取数据失败:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 数组/对象引用
|
||||
|
||||
```javascript
|
||||
// ❌ 直接赋值会复制引用
|
||||
const arr1 = [1, 2, 3];
|
||||
const arr2 = arr1;
|
||||
arr2.push(4); // arr1 也会被修改
|
||||
|
||||
// ✅ 创建副本
|
||||
const arr2 = [...arr1]; // 或 arr1.slice()
|
||||
|
||||
// 对象
|
||||
const obj1 = { a: 1 };
|
||||
const obj2 = { ...obj1 }; // 或 Object.assign({}, obj1)
|
||||
```
|
||||
|
||||
## 并发错误模式
|
||||
|
||||
### 1. 死锁
|
||||
|
||||
```python
|
||||
# ❌ 可能死锁
|
||||
import threading
|
||||
|
||||
lock1 = threading.Lock()
|
||||
lock2 = threading.Lock()
|
||||
|
||||
def thread1():
|
||||
with lock1:
|
||||
with lock2:
|
||||
# 操作
|
||||
|
||||
def thread2():
|
||||
with lock2:
|
||||
with lock1: # 死锁
|
||||
# 操作
|
||||
```
|
||||
|
||||
### 2. 数据竞争
|
||||
|
||||
```python
|
||||
# ❌ 多个线程同时修改共享变量
|
||||
counter = 0
|
||||
|
||||
def increment():
|
||||
global counter
|
||||
counter += 1 # 非原子操作
|
||||
|
||||
# ✅ 使用锁
|
||||
counter = 0
|
||||
lock = threading.Lock()
|
||||
|
||||
def increment():
|
||||
global counter
|
||||
with lock:
|
||||
counter += 1
|
||||
```
|
||||
|
||||
## 预防措施
|
||||
|
||||
1. **使用类型检查**:TypeScript、Python 类型注解
|
||||
2. **编写单元测试**:覆盖边界条件
|
||||
3. **使用静态分析工具**:pylint、eslint
|
||||
4. **代码审查**:让他人检查代码
|
||||
5. **使用防御性编程**:验证输入、处理异常
|
||||
@@ -0,0 +1,29 @@
|
||||
# Debugging Tools
|
||||
|
||||
## Python
|
||||
```bash
|
||||
python -m pdb script.py
|
||||
python -m traceback your_script.py
|
||||
pytest -x -vv tests/test_target.py
|
||||
```
|
||||
|
||||
## Node.js
|
||||
```bash
|
||||
node --inspect-brk app.js
|
||||
node --trace-warnings app.js
|
||||
npm test -- --runInBand
|
||||
```
|
||||
|
||||
## Git
|
||||
```bash
|
||||
git bisect start
|
||||
git bisect bad
|
||||
git bisect good <known-good-commit>
|
||||
```
|
||||
|
||||
## Shell
|
||||
```bash
|
||||
bash -n script.sh
|
||||
bash -x script.sh
|
||||
shellcheck script.sh
|
||||
```
|
||||
@@ -0,0 +1,14 @@
|
||||
# JavaScript / TypeScript Error Guide
|
||||
|
||||
## Frequent failure classes
|
||||
- `TypeError` from undefined/null access
|
||||
- async error swallowing in `Promise` chains
|
||||
- `this` binding mismatches
|
||||
- stale closure bugs in hooks or event handlers
|
||||
- ESM / CJS import mismatches
|
||||
|
||||
## Minimum debugging flow
|
||||
1. copy the full stack trace,
|
||||
2. identify whether the failure is runtime, bundler, or type-level,
|
||||
3. confirm the failing object/value before changing logic,
|
||||
4. reproduce with the smallest possible input.
|
||||
@@ -0,0 +1,311 @@
|
||||
# Python 错误详解
|
||||
|
||||
## 常见内置异常类型
|
||||
|
||||
### 1. SyntaxError(语法错误)
|
||||
|
||||
**特征**:代码无法解析,在运行前就被检测到
|
||||
|
||||
**常见原因**:
|
||||
- 括号不匹配
|
||||
- 缺少冒号
|
||||
- 缩进不正确
|
||||
- 引号不匹配
|
||||
|
||||
**示例**:
|
||||
```python
|
||||
# ❌ 缺少冒号
|
||||
if True
|
||||
print("missing colon")
|
||||
|
||||
# ✅ 正确
|
||||
if True:
|
||||
print("has colon")
|
||||
```
|
||||
|
||||
### 2. IndentationError(缩进错误)
|
||||
|
||||
**特征**:缩进不一致或使用错误的缩进
|
||||
|
||||
**常见原因**:
|
||||
- 混用 Tab 和空格
|
||||
- 缩进级别不正确
|
||||
|
||||
**示例**:
|
||||
```python
|
||||
# ❌ 混用空格和 Tab
|
||||
def test():
|
||||
print("mixed") # Tab
|
||||
print("spaces") # 空格
|
||||
|
||||
# ✅ 统一使用 4 个空格
|
||||
def test():
|
||||
print("spaces")
|
||||
print("consistent")
|
||||
```
|
||||
|
||||
### 3. NameError(名称错误)
|
||||
|
||||
**特征**:变量或函数名不存在
|
||||
|
||||
**常见原因**:
|
||||
- 变量未定义就使用
|
||||
- 函数名拼写错误
|
||||
- 变量作用域问题
|
||||
|
||||
**示例**:
|
||||
```python
|
||||
# ❌ 变量未定义
|
||||
print(undefined_var)
|
||||
|
||||
# ✅ 先定义再使用
|
||||
my_var = 42
|
||||
print(my_var)
|
||||
```
|
||||
|
||||
### 4. TypeError(类型错误)
|
||||
|
||||
**特征**:操作或函数应用于错误的数据类型
|
||||
|
||||
**常见原因**:
|
||||
- 拼接不同类型
|
||||
- 函数参数类型错误
|
||||
- 对不支持的操作使用运算符
|
||||
|
||||
**示例**:
|
||||
```python
|
||||
# ❌ 拼接字符串和数字
|
||||
result = "Value: " + 42
|
||||
|
||||
# ✅ 转换类型
|
||||
result = "Value: " + str(42)
|
||||
# 或使用 f-string
|
||||
result = f"Value: {42}"
|
||||
```
|
||||
|
||||
### 5. AttributeError(属性错误)
|
||||
|
||||
**特征**:对象没有指定的属性或方法
|
||||
|
||||
**常见原因**:
|
||||
- 属性名拼写错误
|
||||
- 对象类型不是预期的
|
||||
- 大小写错误
|
||||
|
||||
**示例**:
|
||||
```python
|
||||
# ❌ 列表没有 append 以外的方法
|
||||
my_list = [1, 2, 3]
|
||||
my_list.push(4) # 列表没有 push 方法
|
||||
|
||||
# ✅ 使用正确的方法
|
||||
my_list.append(4)
|
||||
```
|
||||
|
||||
### 6. KeyError(键错误)
|
||||
|
||||
**特征**:字典中不存在指定的键
|
||||
|
||||
**常见原因**:
|
||||
- 键名拼写错误
|
||||
- 键不存在于字典中
|
||||
|
||||
**示例**:
|
||||
```python
|
||||
data = {"name": "Alice"}
|
||||
|
||||
# ❌ 直接访问不存在的键
|
||||
age = data["age"] # KeyError
|
||||
|
||||
# ✅ 使用 get() 方法
|
||||
age = data.get("age", 0) # 返回默认值 0
|
||||
```
|
||||
|
||||
### 7. IndexError(索引错误)
|
||||
|
||||
**特征**:序列索引超出范围
|
||||
|
||||
**常见原因**:
|
||||
- 索引为负数(除非是有意为之)
|
||||
- 索引大于序列长度-1
|
||||
- 序列为空时访问索引
|
||||
|
||||
**示例**:
|
||||
```python
|
||||
items = [1, 2, 3]
|
||||
|
||||
# ❌ 索引超出范围
|
||||
item = items[5] # IndexError
|
||||
|
||||
# ✅ 检查长度后再访问
|
||||
if len(items) > 5:
|
||||
item = items[5]
|
||||
else:
|
||||
item = None
|
||||
```
|
||||
|
||||
### 8. ValueError(值错误)
|
||||
|
||||
**特征**:参数类型正确但值不合适
|
||||
|
||||
**常见原因**:
|
||||
- 字符串转整数失败
|
||||
- 数学运算值域错误
|
||||
- 参数值不在允许范围内
|
||||
|
||||
**示例**:
|
||||
```python
|
||||
# ❌ 无法转换为整数
|
||||
num = int("abc")
|
||||
|
||||
# ✅ 处理可能的错误
|
||||
try:
|
||||
num = int(input())
|
||||
except ValueError:
|
||||
num = 0
|
||||
```
|
||||
|
||||
### 9. ImportError / ModuleNotFoundError(导入错误)
|
||||
|
||||
**特征**:无法导入模块
|
||||
|
||||
**常见原因**:
|
||||
- 模块未安装
|
||||
- 模块路径不在 PYTHONPATH 中
|
||||
- 模块名拼写错误
|
||||
|
||||
**示例**:
|
||||
```python
|
||||
# ❌ 模块未安装
|
||||
import missing_module
|
||||
|
||||
# 解决方法:安装模块
|
||||
# pip install missing-module
|
||||
```
|
||||
|
||||
### 10. FileNotFoundError(文件未找到错误)
|
||||
|
||||
**特征**:尝试打开不存在的文件
|
||||
|
||||
**常见原因**:
|
||||
- 文件路径错误
|
||||
- 文件不存在
|
||||
- 相对路径使用错误
|
||||
|
||||
**示例**:
|
||||
```python
|
||||
# ❌ 文件不存在
|
||||
with open("missing.txt") as f:
|
||||
content = f.read()
|
||||
|
||||
# ✅ 使用 try-except 或检查文件存在
|
||||
try:
|
||||
with open("file.txt") as f:
|
||||
content = f.read()
|
||||
except FileNotFoundError:
|
||||
content = ""
|
||||
```
|
||||
|
||||
## 异常处理最佳实践
|
||||
|
||||
### 1. 捕获具体异常
|
||||
|
||||
```python
|
||||
# ❌ 捕获所有异常(不良实践)
|
||||
try:
|
||||
result = dangerous_operation()
|
||||
except:
|
||||
pass
|
||||
|
||||
# ✅ 捕获具体异常
|
||||
try:
|
||||
result = dangerous_operation()
|
||||
except (ValueError, TypeError) as e:
|
||||
logger.error(f"操作失败: {e}")
|
||||
```
|
||||
|
||||
### 2. 使用 finally 清理资源
|
||||
|
||||
```python
|
||||
try:
|
||||
file = open("data.txt", "r")
|
||||
content = file.read()
|
||||
except FileNotFoundError:
|
||||
content = ""
|
||||
finally:
|
||||
# 无论是否发生异常都会执行
|
||||
if 'file' in locals():
|
||||
file.close()
|
||||
```
|
||||
|
||||
### 3. 使用上下文管理器
|
||||
|
||||
```python
|
||||
# ✅ 推荐:使用 with 语句
|
||||
with open("data.txt", "r") as file:
|
||||
content = file.read()
|
||||
# 文件会自动关闭
|
||||
```
|
||||
|
||||
### 4. 链式异常
|
||||
|
||||
```python
|
||||
try:
|
||||
process_data(data)
|
||||
except ValueError as e:
|
||||
# 使用 raise from 保留原始异常
|
||||
raise RuntimeError("数据处理失败") from e
|
||||
```
|
||||
|
||||
## 调试技巧
|
||||
|
||||
### 1. 使用 traceback 模块
|
||||
|
||||
```python
|
||||
import traceback
|
||||
|
||||
try:
|
||||
risky_operation()
|
||||
except Exception:
|
||||
# 打印完整的堆栈跟踪
|
||||
traceback.print_exc()
|
||||
```
|
||||
|
||||
### 2. 使用 pdb 调试器
|
||||
|
||||
```python
|
||||
import pdb
|
||||
|
||||
# 在代码中设置断点
|
||||
pdb.set_trace()
|
||||
|
||||
# 或使用 breakpoint() (Python 3.7+)
|
||||
breakpoint()
|
||||
```
|
||||
|
||||
### 3. 使用 logging 模块
|
||||
|
||||
```python
|
||||
import logging
|
||||
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
logger.debug("调试信息")
|
||||
logger.info("普通信息")
|
||||
logger.warning("警告")
|
||||
logger.error("错误")
|
||||
logger.critical("严重错误")
|
||||
```
|
||||
|
||||
## 常见错误排查清单
|
||||
|
||||
- [ ] 检查拼写(变量名、函数名、属性名)
|
||||
- [ ] 检查数据类型(使用 type() 函数)
|
||||
- [ ] 检查变量值(使用 print() 或调试器)
|
||||
- [ ] 检查索引和键是否在范围内
|
||||
- [ ] 检查文件路径是否正确
|
||||
- [ ] 检查缩进是否一致
|
||||
- [ ] 检查括号、引号是否匹配
|
||||
- [ ] 检查是否正确导入了模块
|
||||
- [ ] 检查异常是否被正确处理
|
||||
@@ -0,0 +1,236 @@
|
||||
# Bash/Zsh 脚本错误详解
|
||||
|
||||
## 常见错误类型
|
||||
|
||||
### 1. Command Not Found
|
||||
|
||||
**特征**:bash: command: command not found
|
||||
|
||||
**常见原因**:
|
||||
- 命令拼写错误
|
||||
- 命令未安装
|
||||
- PATH 环境变量不正确
|
||||
- 脚本 shebang 错误
|
||||
|
||||
**示例**:
|
||||
```bash
|
||||
# 拼写错误
|
||||
pyhon script.py # command not found
|
||||
|
||||
# 正确拼写
|
||||
python script.py
|
||||
|
||||
# PATH 问题
|
||||
/usr/local/bin/mycommand # 如果 PATH 不包含 /usr/local/bin
|
||||
|
||||
# 使用完整路径或添加到 PATH
|
||||
export PATH="/usr/local/bin:$PATH"
|
||||
mycommand
|
||||
```
|
||||
|
||||
### 2. Syntax Error
|
||||
|
||||
**特征**:syntax error near unexpected token
|
||||
|
||||
**常见原因**:
|
||||
- 缺少 then/fi/done/ esac
|
||||
- 括号不匹配
|
||||
- 操作符缺少空格
|
||||
|
||||
**示例**:
|
||||
```bash
|
||||
# 缺少 then
|
||||
if [ 1 -eq 1 ]
|
||||
echo "yes" # syntax error
|
||||
|
||||
# 正确
|
||||
if [ 1 -eq 1 ]; then
|
||||
echo "yes"
|
||||
fi
|
||||
```
|
||||
|
||||
### 3. Permission Denied
|
||||
|
||||
**特征**:bash: ./script.sh: Permission denied
|
||||
|
||||
**解决方案**:
|
||||
```bash
|
||||
# 添加执行权限
|
||||
chmod +x script.sh
|
||||
|
||||
# 或使用 bash 运行
|
||||
bash script.sh
|
||||
```
|
||||
|
||||
## 调试技巧
|
||||
|
||||
### 1. 使用 set -x 追踪执行
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
set -x # 启用命令追踪
|
||||
|
||||
name="John"
|
||||
echo "Hello $name"
|
||||
|
||||
# 输出:
|
||||
# + name=John
|
||||
# + echo 'Hello John'
|
||||
# Hello John
|
||||
```
|
||||
|
||||
### 2. 使用 set -e 遇错退出
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
set -e # 任何命令失败时退出
|
||||
|
||||
cd /nonexistent # 脚本在此处退出
|
||||
echo "This won't run"
|
||||
```
|
||||
|
||||
### 3. 使用 set -u 检测未定义变量
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
set -u # 未定义变量时报错
|
||||
|
||||
echo $undefined_var # 报错并退出
|
||||
```
|
||||
|
||||
### 4. 组合使用调试选项
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
set -xeuo pipefail # 严格模式
|
||||
|
||||
# -x: 打印每个命令
|
||||
# -e: 错误时退出
|
||||
# -u: 未定义变量报错
|
||||
# -o pipefail: 管道失败则失败
|
||||
```
|
||||
|
||||
### 5. 使用 trap 捕获错误
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# 在脚本退出时执行清理
|
||||
trap 'echo "Script exited with code $?"' EXIT
|
||||
|
||||
# 在错误时执行
|
||||
trap 'echo "Error on line $LINENO"' ERR
|
||||
|
||||
# 在中断时执行
|
||||
trap 'echo "Interrupted"; cleanup' INT
|
||||
```
|
||||
|
||||
## 错误处理模式
|
||||
|
||||
### 1. 检查命令退出码
|
||||
|
||||
```bash
|
||||
# 检查上一个命令是否成功
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "Success"
|
||||
else
|
||||
echo "Failed"
|
||||
fi
|
||||
|
||||
# 或使用 ||
|
||||
command || { echo "Failed"; exit 1; }
|
||||
|
||||
# 或使用 &&
|
||||
command && echo "Success" || echo "Failed"
|
||||
```
|
||||
|
||||
### 2. 使用函数封装错误处理
|
||||
|
||||
```bash
|
||||
# 定义错误处理函数
|
||||
die() {
|
||||
local message=$1
|
||||
echo "Error: $message" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
# 使用
|
||||
[ -f "$file" ] || die "File not found: $file"
|
||||
```
|
||||
|
||||
### 3. 验证输入参数
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# 检查参数数量
|
||||
[ $# -ge 1 ] || die "Usage: $0 <arg1> [arg2]"
|
||||
|
||||
# 检查文件存在
|
||||
[ -f "$1" ] || die "File not found: $1"
|
||||
|
||||
# 检查目录存在
|
||||
[ -d "$2" ] || die "Directory not found: $2"
|
||||
```
|
||||
|
||||
## 最佳实践
|
||||
|
||||
### 1. 始终使用 shebang
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# 或
|
||||
#!/usr/bin/env bash
|
||||
```
|
||||
|
||||
### 2. 使用 set -euo pipefail
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
```
|
||||
|
||||
### 3. 引用所有变量
|
||||
|
||||
```bash
|
||||
# 除非确定变量不包含空格或通配符
|
||||
echo "$var"
|
||||
```
|
||||
|
||||
### 4. 使用 [[ ]] 而不是 [ ]
|
||||
|
||||
```bash
|
||||
# [[ 更强大且更安全
|
||||
if [[ $name == "John" ]]; then
|
||||
if [[ -f $file && $size -gt 100 ]]; then
|
||||
```
|
||||
|
||||
### 5. 使用 $(command) 而不是反引号
|
||||
|
||||
```bash
|
||||
# $() 更易读且可嵌套
|
||||
result=$(command1 $(command2))
|
||||
```
|
||||
|
||||
### 6. 使用函数组织代码
|
||||
|
||||
```bash
|
||||
my_function() {
|
||||
local arg1=$1
|
||||
local arg2=$2
|
||||
# 函数体
|
||||
}
|
||||
|
||||
my_function "value1" "value2"
|
||||
```
|
||||
|
||||
## ShellCheck 静态分析
|
||||
|
||||
ShellCheck 是一个 Shell 脚本静态分析工具:
|
||||
|
||||
```bash
|
||||
# 安装 ShellCheck
|
||||
brew install shellcheck # macOS
|
||||
apt install shellcheck # Ubuntu
|
||||
|
||||
# 使用
|
||||
shellcheck script.sh
|
||||
```
|
||||
Reference in New Issue
Block a user