name: shell-scripting-fundamentals user-invocable: false description: 用于编写或修改Bash/shell脚本。涵盖脚本结构、变量、引用、条件语句和循环的现代最佳实践。 allowed-tools:
- Read
- Write
- Edit
- Bash
- Grep
- Glob
Shell脚本基础
编写健壮、可维护的shell脚本的核心模式和最佳实践。
脚本结构
始终以正确的shebang和安全选项开始脚本:
#!/usr/bin/env bash
set -euo pipefail
# 脚本描述在这里
安全选项解释
set -e: 在任何命令失败时退出set -u: 对未定义变量报错set -o pipefail: 如果管道中任何命令失败,则管道失败
变量
声明和赋值
# 在=周围没有空格
name="value"
# 使用readonly定义常量
readonly CONFIG_DIR="/etc/myapp"
# 在函数中使用local
my_function() {
local result="computed"
echo "$result"
}
始终引用变量
# 好 - 防止单词拆分和通配符扩展
echo "$variable"
cp "$source" "$destination"
# 坏 - 可能在空格或特殊字符处中断
echo $variable
cp $source $destination
默认值
# 如果未设置则使用默认值
name="${NAME:-default}"
# 如果未设置或为空则使用默认值
name="${NAME:-}"
# 如果未设置则分配默认值
: "${NAME:=default}"
# 如果未设置则报错
: "${REQUIRED_VAR:?错误: REQUIRED_VAR必须设置}"
条件语句
测试语法
# 现代语法 - 首选
if [[ -f "$file" ]]; then
echo "文件存在"
fi
# 字符串比较
if [[ "$string" == "value" ]]; then
echo "匹配"
fi
# 数字比较
if (( count > 10 )); then
echo "大于10"
fi
# 正则表达式匹配
if [[ "$input" =~ ^[0-9]+$ ]]; then
echo "数字输入"
fi
常见测试运算符
| 运算符 | 描述 |
|---|---|
-f |
文件存在且是常规文件 |
-d |
目录存在 |
-e |
路径存在 |
-r |
可读 |
-w |
可写 |
-x |
可执行 |
-z |
字符串为空 |
-n |
字符串不为空 |
循环
For循环
# 遍历列表
for item in one two three; do
echo "$item"
done
# 遍历文件(使用通配符,而非ls)
for file in *.txt; do
[[ -e "$file" ]] || continue # 处理无匹配的情况
process "$file"
done
# C风格for循环
for (( i = 0; i < 10; i++ )); do
echo "$i"
done
While循环
# 从文件中读取行
while IFS= read -r line; do
echo "$line"
done < "$filename"
# 使用进程替换读取
while IFS= read -r line; do
echo "$line"
done < <(some_command)
数组
# 声明数组
declare -a files=()
# 添加元素
files+=("file1.txt")
files+=("file2.txt")
# 遍历所有元素
for file in "${files[@]}"; do
echo "$file"
done
# 获取数组长度
echo "${#files[@]}"
# 通过索引访问
echo "${files[0]}"
命令替换
# 现代语法 - 首选
result=$(command)
# 嵌套替换
result=$(echo $(date))
# 避免旧式反引号
result=`command` # 不要使用这个
函数
# 函数定义
process_file() {
local file="$1"
local output_dir="${2:-./output}"
if [[ ! -f "$file" ]]; then
echo "错误: 文件未找到: $file" >&2
return 1
fi
# 处理文件
cp "$file" "$output_dir/"
}
# 带参数调用
process_file "input.txt" "/tmp/output"
最佳实践总结
- 始终使用
#!/usr/bin/env bash以获得可移植性 - 启用严格模式:
set -euo pipefail - 引用所有变量扩展
- 使用
[[ ]]而非[ ]进行测试 - 使用
$(command)而非反引号 - 在函数中声明局部变量
- 对项目列表使用数组
- 使用前检查命令存在:
command -v cmd >/dev/null