完整bash语法教程:从零到专家
第一部分:bash基础概念
1. 什么是bash?
#!/bin/bash# 这是bash脚本的第一行,称为shebang,指定解释器# bash是Unix/Linux系统的命令行解释器,也是脚本语言# 执行方式1:直接执行(需要执行权限)# chmod +x script.sh# ./script.sh# 执行方式2:使用bash解释器(不需要执行权限)# bash script.sh# 执行方式3:source命令(在当前shell执行)# source script.sh 或 . script.sh2. 基本语法结构
#!/bin/bash# 第一个bash脚本echo"Hello, bash!"# echo命令输出文本echo"当前目录:"$(pwd)# $(command)执行命令并获取输出echo"用户:"$USER# 输出结果:# Hello, bash!# 当前目录: /home/user# 用户: username第二部分:变量和数据类型
1. 变量定义和使用
#!/bin/bash# 1. 定义变量(等号前后不能有空格)name="Alice"age=25salary=5000.50# 2. 使用变量echo"姓名:$name"# 使用$前缀echo"年龄:${age}"# 使用{}明确变量边界echo"薪水:$salary"# 3. 只读变量readonlycountry="China"# country="USA" # 取消注释会报错:readonly variable# 4. 删除变量unsetsalaryecho"薪水:$salary"# 输出空值# 5. 变量类型declare-iint_var=10# 整数变量declare-rconst=100# 只读变量declare-llower="HELLO"# 自动转为小写declare-uupper="hello"# 自动转为大写echo"整数:$int_var"echo"小写:$lower"# 输出: helloecho"大写:$upper"# 输出: HELLO# 输出结果:# 姓名: Alice# 年龄: 25# 薪水: 5000.50# 薪水:# 整数: 10# 小写: hello# 大写: HELLO2. 特殊变量
#!/bin/bash# 运行脚本: bash special_vars.sh arg1 arg2 "arg 3"echo"脚本名:$0"# 脚本名称echo"参数个数:$#"# 参数数量echo"所有参数:$*"# 所有参数作为一个字符串echo"所有参数列表:$@"# 所有参数的列表echo"第一个参数:$1"# 第一个参数echo"第二个参数:$2"# 第二个参数echo"第三个参数:$3"# 第三个参数echo"进程ID:$$"# 当前脚本进程IDecho"退出状态:$?"# 上一条命令的退出状态echo"后台进程:$!"# 最后一个后台进程ID# 测试命令执行状态ls/tmp>/dev/nullecho"ls命令状态:$?"# 0表示成功ls/nonexistent2>/dev/nullecho"错误命令状态:$?"# 非0表示失败# 输出结果:# 脚本名: special_vars.sh# 参数个数: 3# 所有参数: arg1 arg2 arg 3# 所有参数列表: arg1 arg2 arg 3# 第一个参数: arg1# 第二个参数: arg2# 第三个参数: arg 3# 进程ID: 12345# 退出状态: 0# 后台进程:# ls命令状态: 0# 错误命令状态: 23. 数组操作
#!/bin/bash# 1. 定义数组fruits=("apple""banana""orange""grape")# 2. 访问数组元素echo"第一个水果:${fruits[0]}"# 索引从0开始echo"所有水果:${fruits[@]}"# 所有元素echo"水果个数:${#fruits[@]}"# 数组长度# 3. 遍历数组echo"遍历数组:"forfruitin"${fruits[@]}";doecho" -$fruit"done# 4. 修改数组fruits[1]="mango"# 修改元素fruits+=("peach")# 添加元素echo"修改后的数组:${fruits[@]}"# 5. 关联数组(bash 4.0+)declare-A person person["name"]="Bob"person["age"]="30"person["city"]="New York"echo"关联数组:"forkeyin"${!person[@]}";do# 获取所有键echo"$key:${person[$key]}"done# 6. 删除数组元素unsetfruits[2]echo"删除后:${fruits[@]}"# 输出结果:# 第一个水果: apple# 所有水果: apple banana orange grape# 水果个数: 4# 遍历数组:# - apple# - banana# - orange# - grape# 修改后的数组: apple mango orange grape peach# 关联数组:# name: Bob# age: 30# city: New York# 删除后: apple mango grape peach第三部分:字符串操作
1. 字符串基础
#!/bin/bashstr1="Hello"str2="World"# 1. 字符串拼接greeting="$str1,$str2!"echo"拼接:$greeting"# Hello, World!# 2. 字符串长度echo"长度:${#greeting}"# 13# 3. 字符串提取echo"提取1-5:${greeting:0:5}"# Helloecho"提取7-end:${greeting:7}"# World!# 4. 字符串查找和替换text="I love apples, apples are tasty"echo"原始:$text"echo"替换第一个:${text/apples/oranges}"# I love oranges, apples are tastyecho"替换全部:${text//apples/oranges}"# I love oranges, oranges are tastyecho"替换前缀:${text/#I love/He likes}"# He likes apples, apples are tastyecho"替换后缀:${text/%tasty/delicious}"# I love apples, apples are delicious# 5. 大小写转换mixed="Hello World"echo"大写:${mixed^^}"# HELLO WORLDecho"小写:${mixed,,}"# hello worldecho"首字母大写:${mixed^}"# Hello World# 6. 删除子串path="/usr/local/bin/script.sh"echo"原始路径:$path"echo"删除最短前缀:${path#*/}"# usr/local/bin/script.shecho"删除最长前缀:${path##*/}"# script.shecho"删除最短后缀:${path%/*}"# /usr/local/binecho"删除最长后缀:${path%%/*}"# (空)# 7. 默认值unsetmaybe_emptyecho"默认值:${maybe_empty:-默认文本}"# 默认文本echo"变量值:${maybe_empty}"# (空)maybe_empty="有值"echo"默认值:${maybe_empty:-默认文本}"# 有值# 输出结果:# 拼接: Hello, World!# 长度: 13# 提取1-5: Hello# 提取7-end: World!# 原始: I love apples, apples are tasty# 替换第一个: I love oranges, apples are tasty# 替换全部: I love oranges, oranges are tasty# 替换前缀: He likes apples, apples are tasty# 替换后缀: I love apples, apples are delicious# 大写: HELLO WORLD# 小写: hello world# 首字母大写: Hello World# 原始路径: /usr/local/bin/script.sh# 删除最短前缀: usr/local/bin/script.sh# 删除最长前缀: script.sh# 删除最短后缀: /usr/local/bin# 删除最长后缀:# 默认值: 默认文本# 变量值:# 默认值: 有值第四部分:运算符
1. 算术运算符
#!/bin/basha=10b=3# 1. 使用 $(( ))echo"加法:$((a+b))"# 13echo"减法:$((a-b))"# 7echo"乘法:$((a*b))"# 30echo"除法:$((a/b))"# 3 (整数除法)echo"取余:$((a%b))"# 1echo"指数:$((a**b))"# 1000# 2. 使用 letlet"c = a + b"echo"let结果:$c"# 13# 3. 使用 expr (旧方法)d=$(expr$a + $b)echo"expr结果:$d"# 13# 4. 浮点数运算 (使用bc)e=$(echo"scale=2;$a/$b"|bc)echo"浮点除法:$e"# 3.33# 5. 自增自减((a++))echo"自增后:$a"# 11((b--))echo"自减后:$b"# 2# 6. 位运算x=5# 0101y=3# 0011echo"与运算:$((x&y))"# 1 (0001)echo"或运算:$((x|y))"# 7 (0111)echo"异或:$((x^y))"# 6 (0110)echo"左移:$((x<<1))"# 10 (1010)echo"右移:$((x>>1))"# 2 (0010)# 输出结果:# 加法: 13# 减法: 7# 乘法: 30# 除法: 3# 取余: 1# 指数: 1000# let结果: 13# expr结果: 13# 浮点除法: 3.33# 自增后: 11# 自减后: 2# 与运算: 1# 或运算: 7# 异或: 6# 左移: 10# 右移: 22. 关系运算符
#!/bin/bashx=10y=20z=10# 数值比较echo"数值比较:"echo"x > y:$((x>y))"# 0 (假)echo"x < y:$((x<y))"# 1 (真)echo"x >= z:$((x>=z))"# 1echo"x <= z:$((x<=z))"# 1echo"x == z:$((x==z))"# 1echo"x != y:$((x!=y))"# 1# 字符串比较str1="hello"str2="world"str3="hello"echo-e"\n字符串比较:"if[["$str1"=="$str3"]];thenecho"str1等于str3"# 输出fiif[["$str1"!="$str2"]];thenecho"str1不等于str2"# 输出fiif[[-z""]];thenecho"空字符串"# 输出fiif[[-n"$str1"]];thenecho"非空字符串"# 输出fi# 输出结果:# 数值比较:# x > y: 0# x < y: 1# x >= z: 1# x <= z: 1# x == z: 1# x != y: 1## 字符串比较:# str1等于str3# str1不等于str2# 空字符串# 非空字符串3. 逻辑运算符
#!/bin/basha=10b=20# 逻辑运算if[[$a-lt15&&$b-gt15]];thenecho"a<15 且 b>15"# 输出fiif[[$a-gt15||$b-gt15]];thenecho"a>15 或 b>15"# 输出fiif[[!$a-gt15]];thenecho"a不大于15"# 输出fi# 短路运算[[-f"/etc/passwd"]]&&echo"文件存在"# 输出[[-f"/nonexistent"]]||echo"文件不存在"# 输出# 输出结果:# a<15 且 b>15# a>15 或 b>15# a不大于15# 文件存在# 文件不存在第五部分:控制结构
1. 条件判断
#!/bin/bash# 1. if-elif-elsenum=75if[[$num-ge90]];thenecho"优秀"elif[[$num-ge80]];thenecho"良好"elif[[$num-ge70]];thenecho"中等"# 输出elif[[$num-ge60]];thenecho"及格"elseecho"不及格"fi# 2. case语句fruit="apple"case$fruitin"apple")echo"这是苹果"# 输出;;"banana")echo"这是香蕉";;"orange"|"lemon")echo"这是柑橘类";;*)echo"未知水果";;esac# 3. 测试命令 test 和 [ ]file="/etc/passwd"iftest-f"$file";thenecho"文件存在(使用test)"# 输出fiif[-f"$file"];thenecho"文件存在(使用[ ])"# 输出fiif[[-f"$file"&&-r"$file"]];thenecho"文件存在且可读"# 输出fi# 输出结果:# 中等# 这是苹果# 文件存在(使用test)# 文件存在(使用[ ])# 文件存在且可读2. 循环结构
#!/bin/bash# 1. for循环echo"for循环示例:"# 方式1:遍历列表foriin12345;doecho"数字:$i"done# 方式2:遍历命令输出forfilein$(ls/tmp|head-3);doecho"文件:$file"done# 方式3:C语言风格for((i=1;i<=3;i++));doecho"计数:$i"done# 2. while循环echo-e"\nwhile循环示例:"count=1while[[$count-le3]];doecho"while:$count"((count++))done# 3. until循环echo-e"\nuntil循环示例:"num=1until[[$num-gt3]];doecho"until:$num"((num++))done# 4. 无限循环echo-e"\n无限循环(按Ctrl+C停止):"# while true; do# echo "循环中..."# sleep 1# done# 5. 循环控制echo-e"\n循环控制:"foriin{1..10};doif[[$i-eq3]];thencontinue# 跳过本次循环fiif[[$i-eq8]];thenbreak# 跳出循环fiecho"处理:$i"done# 输出结果:# for循环示例:# 数字: 1# 数字: 2# 数字: 3# 数字: 4# 数字: 5# 文件: file1# 文件: file2# 文件: file3# 计数: 1# 计数: 2# 计数: 3## while循环示例:# while: 1# while: 2# while: 3## until循环示例:# until: 1# until: 2# until: 3## 循环控制:# 处理: 1# 处理: 2# 处理: 4# 处理: 5# 处理: 6# 处理: 7第六部分:函数
1. 函数基础
#!/bin/bash# 1. 函数定义say_hello(){localname=$1# local定义局部变量echo"Hello,$name!"return0# 返回状态码(0-255)}# 2. 调用函数say_hello"Alice"# 传递参数echo"返回值:$?"# 获取返回值# 3. 带返回值的函数add(){localsum=$(($1+$2))echo$sum# 通过echo返回值(可以返回字符串)}result=$(add1020)echo"加法结果:$result"# 4. 参数处理process_args(){echo"参数个数:$#"echo"所有参数:$@"echo"第一个参数:$1"echo"第二个参数:$2"# 遍历所有参数forargin"$@";doecho"参数:$arg"done}process_args"arg1""arg2""arg3"# 5. 递归函数factorial(){localn=$1if[[$n-le1]];thenecho1elselocalprev=$(factorial$((n-1)))echo$((n*prev))fi}echo"5的阶乘:$(factorial5)"# 输出结果:# Hello, Alice!# 返回值: 0# 加法结果: 30# 参数个数: 3# 所有参数: arg1 arg2 arg3# 第一个参数: arg1# 第二个参数: arg2# 参数: arg1# 参数: arg2# 参数: arg3# 5的阶乘: 120第七部分:输入输出
1. 标准输入输出
#!/bin/bash# 1. 读取用户输入echo-n"请输入您的名字: "readusernameecho"你好,$username!"# 2. 读取多个值echo-n"请输入姓名和年龄: "readname ageecho"姓名:$name, 年龄:$age"# 3. 静默读取(用于密码)echo-n"请输入密码: "read-s passwordecho-e"\n密码已接收"# 4. 超时设置echo-n"请在5秒内输入: "read-t5timeout_input||echo"时间到!"echo"输入:$timeout_input"# 5. 读取文件行echo"读取/etc/passwd前3行:"count=0whileIFS=read-r line&&[[$count-lt3]];doecho"行$((count+1)):$line"((count++))done</etc/passwd# 6. 重定向echo"重定向示例:"echo"这是标准输出">output.txt# 覆盖写入echo"这是追加内容">>output.txt# 追加写入catoutput.txt# 错误重定向ls/nonexistent2>error.log# 错误输出到文件ls/tmp&>all_output.log# 所有输出到文件# 7. 管道echo-e"\n管道示例:"echo-e"apple\nbanana\ncherry\napple"|sort|uniq-c# 8. Here Documentcat<<EOF 这是Here Document 可以输入多行文本 直到遇到结束标记EOF EOF# 输出结果:# 请输入您的名字: Alice# 你好, Alice!# 请输入姓名和年龄: Bob 25# 姓名: Bob, 年龄: 25# 请输入密码:# 密码已接收# 请在5秒内输入: hello# 输入: hello# 读取/etc/passwd前3行:# 行 1: root:x:0:0:root:/root:/bin/bash# 行 2: daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin# 行 3: bin:x:2:2:bin:/bin:/usr/sbin/nologin# 重定向示例:# 这是标准输出# 这是追加内容## 管道示例:# 2 apple# 1 banana# 1 cherry# 这是Here Document# 可以输入多行文本# 直到遇到结束标记EOF第八部分:高级特性
1. 进程和作业控制
#!/bin/bash# 1. 后台运行echo"启动后台进程:"sleep5&bg_pid=$!echo"后台进程PID:$bg_pid"# 2. 等待进程echo"等待后台进程完成..."wait$bg_pidecho"后台进程完成"# 3. 作业控制echo-e"\n作业控制:"sleep3&sleep2&jobs# 显示作业列表fg%1# 将作业1调到前台jobs# 再次显示# 4. 信号处理trap"echo '收到SIGINT信号'; exit 1"SIGINTecho"按Ctrl+C测试信号处理"# sleep 10 # 取消注释测试# 5. 子shellecho-e"\n子shell示例:"(echo"子shell中"cd/tmppwd# 输出: /tmp)pwd# 输出原始目录# 输出结果:# 启动后台进程:# 后台进程PID: 12345# 等待后台进程完成...# 后台进程完成## 作业控制:# [1]- 运行中 sleep 3 &# [2]+ 运行中 sleep 2 &## 按Ctrl+C测试信号处理## 子shell示例:# 子shell中# /tmp# /home/user2. 调试和错误处理
#!/bin/bash# 1. 调试模式set-x# 开启调试,显示执行的命令echo"调试模式开始"debug_var="test"echo"变量值:$debug_var"set+x# 关闭调试echo"调试模式结束"# 2. 错误处理选项set-e# 遇到错误立即退出set-u# 使用未定义变量时报错set-o pipefail# 管道中任一命令失败则整个管道失败# 3. 自定义错误处理error_handler(){echo"错误发生在第$1行"echo"命令:$2"echo"退出码:$3"}trap'error_handler${LINENO}"$BASH_COMMAND"$?'ERR# 测试错误ls/nonexistent# 这会产生错误# 4. 脚本选项处理whilegetopts":a:b:c"opt;docase$optina)echo"选项-a的值:$OPTARG";;b)echo"选项-b的值:$OPTARG";;c)echo"选项-c被设置";;\?)echo"无效选项: -$OPTARG";;:)echo"选项-$OPTARG需要参数";;esacdone# 运行: bash script.sh -a value1 -b value2 -c# 输出结果:# + echo '调试模式开始'# 调试模式开始# + debug_var=test# + echo '变量值: test'# 变量值: test# + set +x# 调试模式结束# 错误发生在第 27 行# 命令: ls /nonexistent# 退出码: 2# 选项-a的值: value1# 选项-b的值: value2# 选项-c被设置第九部分:实战示例
1. 系统监控脚本
#!/bin/bash# 系统监控脚本# 颜色定义RED='\033[0;31m'GREEN='\033[0;32m'YELLOW='\033[1;33m'NC='\033[0m'# No Color# 获取系统信息get_system_info(){echo-e"${GREEN}====== 系统信息 ======${NC}"# 主机信息echo-e"${YELLOW}主机名:${NC}$(hostname)"echo-e"${YELLOW}系统版本:${NC}$(cat/etc/os-release|grepPRETTY_NAME|cut-d'"'-f2)"echo-e"${YELLOW}内核版本:${NC}$(uname-r)"echo-e"${YELLOW}运行时间:${NC}$(uptime-p|sed's/up //')"# CPU信息localload=$(uptime|awk-F'load average:''{print$2}')echo-e"${YELLOW}负载:${NC}$load"# 内存信息localmem_total=$(free-h|awk'/Mem:/ {print$2}')localmem_used=$(free-h|awk'/Mem:/ {print$3}')localmem_percent=$(free|awk'/Mem:/ {printf "%.1f",$3/$2*100}')echo-e"${YELLOW}内存使用:${NC}$mem_used/$mem_total($mem_percent%)"# 磁盘信息localdisk_usage=$(df-h /|awk'NR==2 {print$5}')echo-e"${YELLOW}根分区使用:${NC}$disk_usage"# 进程信息localprocess_count=$(psaux|wc-l)echo-e"${YELLOW}进程数:${NC}$((process_count-1))"# 用户信息localuser_count=$(who|wc-l)echo-e"${YELLOW}登录用户:${NC}$user_count"}# 检查服务状态check_service(){localservice=$1ifsystemctl is-active --quiet$service;thenecho-e"$service:${GREEN}运行中${NC}"elseecho-e"$service:${RED}停止${NC}"fi}# 检查端口check_port(){localport=$1ifnetstat-tuln|grep":$port">/dev/null;thenecho-e" 端口$port:${GREEN}监听中${NC}"elseecho-e" 端口$port:${RED}未监听${NC}"fi}# 主函数main(){get_system_infoecho-e"\n${GREEN}====== 服务状态 ======${NC}"check_service"sshd"check_service"nginx"check_service"mysql"echo-e"\n${GREEN}====== 端口状态 ======${NC}"check_port22check_port80check_port3306# 告警检查echo-e"\n${GREEN}====== 告警检查 ======${NC}"# 内存检查localmem_percent=$(free|awk'/Mem:/ {printf "%.0f",$3/$2*100}')if[[$mem_percent-gt90]];thenecho-e"${RED}警告: 内存使用率超过90%!${NC}"elif[[$mem_percent-gt80]];thenecho-e"${YELLOW}注意: 内存使用率超过80%${NC}"elseecho-e"${GREEN}内存使用正常${NC}"fi# 磁盘检查localdisk_percent=$(df/|awk'NR==2 {print$5}'|tr-d'%')if[[$disk_percent-gt90]];thenecho-e"${RED}警告: 磁盘使用率超过90%!${NC}"elif[[$disk_percent-gt80]];thenecho-e"${YELLOW}注意: 磁盘使用率超过80%${NC}"elseecho-e"${GREEN}磁盘使用正常${NC}"fi}# 运行主函数main2. 备份脚本
#!/bin/bash# 文件备份脚本CONFIG_FILE="backup.conf"LOG_FILE="backup.log"# 读取配置文件if[[-f"$CONFIG_FILE"]];thensource"$CONFIG_FILE"else# 默认配置BACKUP_DIR="./backups"SOURCE_DIRS=(".""/etc")RETENTION_DAYS=7COMPRESS=truefi# 创建备份目录mkdir-p"$BACKUP_DIR"# 日志函数log_message(){locallevel=$1localmessage=$2localtimestamp=$(date'+%Y-%m-%d %H:%M:%S')echo"[$timestamp] [$level]$message"|tee-a"$LOG_FILE"}# 错误处理handle_error(){log_message"ERROR""备份失败:$1"exit1}# 执行备份perform_backup(){localbackup_name="backup_$(date'+%Y%m%d_%H%M%S')"localbackup_path="$BACKUP_DIR/$backup_name"log_message"INFO""开始备份:$backup_name"# 创建临时目录mkdir-p"$backup_path"||handle_error"无法创建备份目录"# 备份每个目录fordirin"${SOURCE_DIRS[@]}";doif[[-d"$dir"]];thenlog_message"INFO""备份目录:$dir"localdir_name=$(basename"$dir")if[["$dir_name"=="."]];thendir_name="current"ficp-r"$dir""$backup_path/$dir_name"||\log_message"WARNING""无法备份$dir"elselog_message"WARNING""目录不存在:$dir"fidone# 压缩备份if[["$COMPRESS"=="true"]];thenlog_message"INFO""压缩备份文件..."tar-czf"$backup_path.tar.gz"-C"$BACKUP_DIR""$backup_name"||\handle_error"压缩失败"# 删除未压缩的目录rm-rf"$backup_path"backup_path="$backup_path.tar.gz"filog_message"INFO""备份完成:$(du-h"$backup_path"|cut-f1)"echo"$backup_path"}# 清理旧备份cleanup_old_backups(){log_message"INFO""清理$RETENTION_DAYS天前的备份..."localdeleted_count=0forbackupin"$BACKUP_DIR"/backup_*;doif[[-f"$backup"]]||[[-d"$backup"]];thenlocalbackup_age=$((($(date+%s)-$(stat-c%Y "$backup"))/86400))if[[$backup_age-gt$RETENTION_DAYS]];thenlog_message"INFO""删除旧备份:$(basename"$backup")(${backup_age}天)"rm-rf"$backup"((deleted_count++))fifidonelog_message"INFO""清理完成,删除了$deleted_count个旧备份"}# 主函数main(){log_message"INFO""=== 备份脚本开始 ==="# 检查源目录localvalid_dirs=0fordirin"${SOURCE_DIRS[@]}";doif[[-d"$dir"]];then((valid_dirs++))fidoneif[[$valid_dirs-eq0]];thenhandle_error"没有有效的源目录"fi# 执行备份backup_file=$(perform_backup)# 清理旧备份cleanup_old_backups# 生成报告localtotal_size=$(du-sh"$BACKUP_DIR"|cut-f1)localbackup_count=$(ls-1"$BACKUP_DIR"/backup_*2>/dev/null|wc-l)log_message"INFO""备份统计:"log_message"INFO"" 备份目录:$BACKUP_DIR"log_message"INFO"" 总大小:$total_size"log_message"INFO"" 备份数量:$backup_count"log_message"INFO"" 最新备份:$(basename"$backup_file")"log_message"INFO""=== 备份脚本结束 ==="# 发送通知(可选)# send_notification "备份完成: $(basename "$backup_file")"}# 运行主函数main"$@"第十部分:最佳实践总结
1. 安全实践
#!/bin/bash# bash最佳实践示例# 1. 使用set命令增强安全性set-euo pipefail# 启用严格模式set-o nounset# 使用未定义变量时报错set-o errexit# 命令失败时退出set-o pipefail# 管道失败时退出# 2. 总是引用变量path="/path with spaces"# 错误: ls $path# 正确:ls"$path"# 3. 使用[[ ]]代替[ ]进行测试if[[-f"$file"&&-r"$file"]];then# 更安全,功能更强大fi# 4. 使用函数提高可重用性log(){locallevel=$1localmessage=$2echo"[$(date)] [$level]$message"}# 5. 使用local定义局部变量calculate(){localresult=$(($1+$2))echo"$result"}# 6. 验证输入validate_input(){if[[-z"$1"]];thenecho"错误: 参数不能为空"exit1fiif[[!"$1"=~ ^[a-zA-Z]+$]];thenecho"错误: 只允许字母"exit1fi}# 7. 使用trap清理资源cleanup(){rm-f"$TEMP_FILE"echo"清理完成"}trapcleanup EXIT# 8. 提供使用帮助usage(){cat<<EOF 用法:$0[选项] <参数> 选项: -h, --help 显示帮助信息 -v, --version 显示版本信息 -d, --debug 启用调试模式 示例:$0--debug test EOF}# 9. 使用getopts处理参数whilegetopts":hvd:"opt;docase$optinh)usage;exit0;;v)echo"版本 1.0.0";exit0;;d)DEBUG="$OPTARG";;\?)echo"无效选项: -$OPTARG">&2;exit1;;:)echo"选项 -$OPTARG需要参数">&2;exit1;;esacdone# 10. 添加脚本头信息:' 脚本名称: best_practice.sh 作者: Your Name 版本: 1.0.0 描述: bash最佳实践示例 创建日期: 2024-01-01 修改历史: 2024-01-01: 创建脚本 '# 11. 使用颜色和格式输出RED='\033[0;31m'GREEN='\033[0;32m'YELLOW='\033[1;33m'NC='\033[0m'echo-e"${GREEN}成功消息${NC}"echo-e"${RED}错误消息${NC}"echo-e"${YELLOW}警告消息${NC}"2. 性能优化
#!/bin/bash# 性能优化示例# 1. 避免不必要的子shell# 不好:result=$(echo"$var"|tr'a-z''A-Z')# 好:result="${var^^}"# 2. 使用内置字符串操作代替外部命令# 不好:length=$(echo"$str"|wc-c)# 好:length=${#str}# 3. 批量处理而不是循环处理# 不好:forfilein*.txt;domv"$file""${file%.txt}.bak"done# 好:rename'.txt''.bak'*.txt# 4. 使用数组代替多个变量# 不好:item1="value1"item2="value2"item3="value3"# 好:items=("value1""value2""value3")# 5. 使用here string代替echo管道# 不好:echo"$data"|grep"pattern"# 好:grep"pattern"<<<"$data"# 6. 缓存命令输出# 不好: 多次执行相同命令if[[$(who|wc-l)-gt5]];thenecho"用户数:$(who|wc-l)"fi# 好: 缓存结果user_count=$(who|wc-l)if[[$user_count-gt5]];thenecho"用户数:$user_count"fi# 7. 使用算术展开代替expr# 不好:result=$(expr$a + $b)# 好:result=$((a+b))# 8. 提前退出减少嵌套# 不好:if[[-f"$file"]];thenif[[-r"$file"]];then# 处理文件fifi# 好:[[-f"$file"]]||exit1[[-r"$file"]]||exit1# 处理文件# 9. 使用函数减少重复代码process_file(){localfile=$1[[-f"$file"]]||return1# 处理逻辑}# 10. 并行处理process_items(){localitem=$1# 处理单个项目}# 顺序处理# for item in "${items[@]}"; do# process_items "$item"# done# 并行处理(如果有大量数据)foritemin"${items[@]}";doprocess_items"$item"&donewait通过这个完整教程,你已经掌握了bash从基础到高级的所有概念。记住:
- 实践是最好的老师- 多写脚本解决实际问题
- 阅读优秀代码- 学习开源项目的bash脚本
- 掌握调试技巧- 使用
set -x、echo调试 - 关注安全性- 避免常见的安全漏洞
- 保持代码简洁- 复杂的逻辑考虑用其他语言实现
祝你成为bash专家!