$npx -y skills add chaterm/terminal-skills --skill shell-scriptingBash Shell 脚本编写
| 1 | # Shell 脚本编写 |
| 2 | |
| 3 | ## 概述 |
| 4 | Bash 脚本编写、调试、最佳实践等技能。 |
| 5 | |
| 6 | ## 基础语法 |
| 7 | |
| 8 | ### 脚本结构 |
| 9 | ```bash |
| 10 | #!/bin/bash |
| 11 | # 脚本描述 |
| 12 | # Author: name |
| 13 | # Date: 2024-01-01 |
| 14 | |
| 15 | set -euo pipefail # 严格模式 |
| 16 | |
| 17 | # 变量定义 |
| 18 | VAR="value" |
| 19 | readonly CONST="constant" |
| 20 | |
| 21 | # 主逻辑 |
| 22 | main() { |
| 23 | echo "Hello, World!" |
| 24 | } |
| 25 | |
| 26 | main "$@" |
| 27 | ``` |
| 28 | |
| 29 | ### 变量 |
| 30 | ```bash |
| 31 | # 定义变量 |
| 32 | name="value" |
| 33 | name='literal value' # 不解析变量 |
| 34 | |
| 35 | # 使用变量 |
| 36 | echo $name |
| 37 | echo ${name} |
| 38 | echo "${name}_suffix" |
| 39 | |
| 40 | # 默认值 |
| 41 | ${var:-default} # 未设置时使用默认值 |
| 42 | ${var:=default} # 未设置时赋值并使用 |
| 43 | ${var:+value} # 已设置时使用 value |
| 44 | ${var:?error message} # 未设置时报错 |
| 45 | |
| 46 | # 字符串操作 |
| 47 | ${#var} # 长度 |
| 48 | ${var:0:5} # 子串 |
| 49 | ${var#pattern} # 删除前缀 |
| 50 | ${var%pattern} # 删除后缀 |
| 51 | ${var/old/new} # 替换 |
| 52 | ``` |
| 53 | |
| 54 | ### 数组 |
| 55 | ```bash |
| 56 | # 定义数组 |
| 57 | arr=(a b c d) |
| 58 | arr[0]="first" |
| 59 | |
| 60 | # 访问数组 |
| 61 | ${arr[0]} # 第一个元素 |
| 62 | ${arr[@]} # 所有元素 |
| 63 | ${#arr[@]} # 数组长度 |
| 64 | ${!arr[@]} # 所有索引 |
| 65 | |
| 66 | # 遍历数组 |
| 67 | for item in "${arr[@]}"; do |
| 68 | echo "$item" |
| 69 | done |
| 70 | ``` |
| 71 | |
| 72 | ## 流程控制 |
| 73 | |
| 74 | ### 条件判断 |
| 75 | ```bash |
| 76 | # if 语句 |
| 77 | if [[ condition ]]; then |
| 78 | commands |
| 79 | elif [[ condition ]]; then |
| 80 | commands |
| 81 | else |
| 82 | commands |
| 83 | fi |
| 84 | |
| 85 | # 条件表达式 |
| 86 | [[ -f file ]] # 文件存在 |
| 87 | [[ -d dir ]] # 目录存在 |
| 88 | [[ -z "$var" ]] # 变量为空 |
| 89 | [[ -n "$var" ]] # 变量非空 |
| 90 | [[ "$a" == "$b" ]] # 字符串相等 |
| 91 | [[ "$a" != "$b" ]] # 字符串不等 |
| 92 | [[ $a -eq $b ]] # 数字相等 |
| 93 | [[ $a -lt $b ]] # 小于 |
| 94 | [[ $a -gt $b ]] # 大于 |
| 95 | |
| 96 | # 逻辑运算 |
| 97 | [[ cond1 && cond2 ]] # 与 |
| 98 | [[ cond1 || cond2 ]] # 或 |
| 99 | [[ ! cond ]] # 非 |
| 100 | ``` |
| 101 | |
| 102 | ### 循环 |
| 103 | ```bash |
| 104 | # for 循环 |
| 105 | for i in 1 2 3 4 5; do |
| 106 | echo $i |
| 107 | done |
| 108 | |
| 109 | for i in {1..10}; do |
| 110 | echo $i |
| 111 | done |
| 112 | |
| 113 | for ((i=0; i<10; i++)); do |
| 114 | echo $i |
| 115 | done |
| 116 | |
| 117 | for file in *.txt; do |
| 118 | echo "$file" |
| 119 | done |
| 120 | |
| 121 | # while 循环 |
| 122 | while [[ condition ]]; do |
| 123 | commands |
| 124 | done |
| 125 | |
| 126 | # 读取文件 |
| 127 | while IFS= read -r line; do |
| 128 | echo "$line" |
| 129 | done < file.txt |
| 130 | |
| 131 | # until 循环 |
| 132 | until [[ condition ]]; do |
| 133 | commands |
| 134 | done |
| 135 | ``` |
| 136 | |
| 137 | ### case 语句 |
| 138 | ```bash |
| 139 | case "$var" in |
| 140 | pattern1) |
| 141 | commands |
| 142 | ;; |
| 143 | pattern2|pattern3) |
| 144 | commands |
| 145 | ;; |
| 146 | *) |
| 147 | default commands |
| 148 | ;; |
| 149 | esac |
| 150 | ``` |
| 151 | |
| 152 | ## 函数 |
| 153 | |
| 154 | ### 定义函数 |
| 155 | ```bash |
| 156 | # 方式1 |
| 157 | function_name() { |
| 158 | local var="local variable" |
| 159 | echo "Arguments: $@" |
| 160 | echo "First arg: $1" |
| 161 | echo "Arg count: $#" |
| 162 | return 0 |
| 163 | } |
| 164 | |
| 165 | # 方式2 |
| 166 | function function_name { |
| 167 | commands |
| 168 | } |
| 169 | |
| 170 | # 调用函数 |
| 171 | function_name arg1 arg2 |
| 172 | |
| 173 | # 获取返回值 |
| 174 | result=$(function_name) |
| 175 | ``` |
| 176 | |
| 177 | ### 常用函数模板 |
| 178 | ```bash |
| 179 | # 日志函数 |
| 180 | log() { |
| 181 | local level=$1 |
| 182 | shift |
| 183 | echo "[$(date '+%Y-%m-%d %H:%M:%S')] [$level] $*" |
| 184 | } |
| 185 | |
| 186 | log INFO "This is info message" |
| 187 | log ERROR "This is error message" |
| 188 | |
| 189 | # 错误处理 |
| 190 | die() { |
| 191 | echo "ERROR: $*" >&2 |
| 192 | exit 1 |
| 193 | } |
| 194 | |
| 195 | # 确认函数 |
| 196 | confirm() { |
| 197 | read -p "$1 [y/N] " response |
| 198 | [[ "$response" =~ ^[Yy]$ ]] |
| 199 | } |
| 200 | |
| 201 | if confirm "Continue?"; then |
| 202 | echo "Proceeding..." |
| 203 | fi |
| 204 | ``` |
| 205 | |
| 206 | ## 输入输出 |
| 207 | |
| 208 | ### 读取输入 |
| 209 | ```bash |
| 210 | # 读取用户输入 |
| 211 | read -p "Enter name: " name |
| 212 | read -sp "Enter password: " password # 隐藏输入 |
| 213 | read -t 10 -p "Quick! " answer # 超时 |
| 214 | |
| 215 | # 读取文件 |
| 216 | while IFS= read -r line; do |
| 217 | echo "$line" |
| 218 | done < file.txt |
| 219 | ``` |
| 220 | |
| 221 | ### 重定向 |
| 222 | ```bash |
| 223 | # 输出重定向 |
| 224 | command > file # 覆盖 |
| 225 | command >> file # 追加 |
| 226 | command 2> error.log # 错误输出 |
| 227 | command > file 2>&1 # 合并输出 |
| 228 | command &> file # 同上 |
| 229 | |
| 230 | # 输入重定向 |
| 231 | command < file |
| 232 | |
| 233 | # Here Document |
| 234 | cat << EOF |
| 235 | 多行文本 |
| 236 | 变量: $var |
| 237 | EOF |
| 238 | |
| 239 | cat << 'EOF' # 不解析变量 |
| 240 | 原始文本 |
| 241 | EOF |
| 242 | ``` |
| 243 | |
| 244 | ## 调试技巧 |
| 245 | |
| 246 | ### 调试选项 |
| 247 | ```bash |
| 248 | # 启用调试 |
| 249 | set -x # 打印执行的命令 |
| 250 | set -v # 打印读取的行 |
| 251 | set -e # 出错即退出 |
| 252 | set -u # 未定义变量报错 |
| 253 | set -o pipefail # 管道错误传递 |
| 254 | |
| 255 | # 组合使用 |
| 256 | set -euxo pipefail |
| 257 | |
| 258 | # 调试特定部分 |
| 259 | set -x |
| 260 | # 调试代码 |
| 261 | set +x |
| 262 | ``` |
| 263 | |
| 264 | ### 调试工具 |
| 265 | ```bash |
| 266 | # 语法检查 |
| 267 | bash -n script.sh |
| 268 | |
| 269 | # 调试运行 |
| 270 | bash -x script.sh |
| 271 | |
| 272 | # shellcheck 静态分析 |
| 273 | shellcheck script.sh |
| 274 | ``` |
| 275 | |
| 276 | ## 最佳实践 |
| 277 | |
| 278 | ### 脚本模板 |
| 279 | ```bash |
| 280 | #!/bin/bash |
| 281 | # |
| 282 | # Script: script_name.sh |
| 283 | # Description: Brief description |
| 284 | # Author: Your Name |
| 285 | # Date: 2024-01-01 |
| 286 | # |
| 287 | |
| 288 | set -euo pipefail |
| 289 | |
| 290 | # Constants |
| 291 | readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" |
| 292 | readonly SCRIPT_NAME="$(basename "$0")" |
| 293 | |
| 294 | # Logging |
| 295 | log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*"; } |
| 296 | error() { echo "[ERROR] $*" >&2; } |
| 297 | die() { error "$*"; exit 1; } |
| 298 | |
| 299 | # Usage |
| 300 | usage() { |
| 301 | cat << EOF |
| 302 | Usage: $SCRIPT_NAME [options] <arguments> |
| 303 | |
| 304 | Options: |
| 305 | -h, --help Show this help message |
| 306 | -v, --verbose Enable verbose mode |
| 307 | |
| 308 | Examples: |
| 309 | $SCRIPT_NAME -v input.txt |
| 310 | EOF |
| 311 | } |
| 312 | |
| 313 | # Parse arguments |
| 314 | parse_args() { |
| 315 | while [[ $# -gt 0 ]]; do |
| 316 | case $1 in |
| 317 | -h|--help) |
| 318 | usage |
| 319 | exit 0 |
| 320 | ;; |
| 321 | -v|-- |