Shell 脚本编程 #
一、Shell 脚本基础 #
1.1 脚本结构 #
bash
#!/bin/bash
# 这是注释
echo "Hello World"
脚本组成:
- Shebang:
#!/bin/bash指定解释器 - 注释:以
#开头 - 命令:可执行的 Shell 命令
1.2 执行脚本 #
bash
# 方法一:添加执行权限
chmod +x script.sh
./script.sh
# 方法二:指定解释器
bash script.sh
sh script.sh
# 方法三:source 执行(在当前 Shell 中执行)
source script.sh
. script.sh
# 方法四:重定向执行
bash < script.sh
1.3 脚本调试 #
bash
# 显示执行的命令
bash -x script.sh
# 检查语法错误
bash -n script.sh
# 显示详细信息
bash -v script.sh
# 在脚本中启用调试
#!/bin/bash
set -x # 开启调试
set +x # 关闭调试
二、变量 #
2.1 变量定义 #
bash
# 定义变量(等号两边不能有空格)
name="John"
age=25
# 使用变量
echo $name
echo ${name}
echo "My name is $name"
# 只读变量
readonly PI=3.14
# 删除变量
unset name
2.2 变量类型 #
bash
# 字符串
str="Hello World"
str='Hello World' # 单引号不解析变量
# 数字
num=123
# 数组
arr=(1 2 3 4 5)
arr[0]=1
arr[1]=2
# 访问数组
echo ${arr[0]} # 第一个元素
echo ${arr[@]} # 所有元素
echo ${arr[*]} # 所有元素
echo ${#arr[@]} # 数组长度
echo ${#arr[*]} # 数组长度
# 关联数组(需要 declare)
declare -A user
user[name]="John"
user[age]=25
echo ${user[name]}
2.3 特殊变量 #
bash
$0 # 脚本名称
$1-$9 # 第 1-9 个参数
${10} # 第 10 个参数
$# # 参数个数
$@ # 所有参数(独立)
$* # 所有参数(整体)
$? # 上一个命令的退出状态
$$ # 当前进程 PID
$! # 后台进程 PID
$- # 当前 Shell 选项
$_ # 上一个命令的最后一个参数
2.4 环境变量 #
bash
# 查看环境变量
env
printenv
# 查看指定环境变量
echo $HOME
echo $PATH
echo $USER
echo $PWD
# 设置环境变量
export MY_VAR="value"
# 在脚本中使用
#!/bin/bash
export PATH="/usr/local/bin:$PATH"
2.5 变量替换 #
bash
# 默认值
echo ${var:-default} # var 未定义或为空,返回 default
echo ${var:=default} # var 未定义或为空,赋值为 default
echo ${var:+value} # var 已定义且非空,返回 value
echo ${var:?error} # var 未定义或为空,输出错误
# 字符串操作
str="Hello World"
echo ${#str} # 字符串长度
echo ${str:0:5} # 截取子串
echo ${str#Hello} # 删除开头匹配
echo ${str%World} # 删除结尾匹配
echo ${str/World/Linux} # 替换第一个匹配
echo ${str//o/O} # 替换所有匹配
echo ${str^} # 首字母大写
echo ${str^^} # 全部大写
echo ${str,} # 首字母小写
echo ${str,,} # 全部小写
三、输入输出 #
3.1 echo 输出 #
bash
# 基本输出
echo "Hello World"
# 不换行
echo -n "Hello"
echo " World"
# 解析转义字符
echo -e "Line1\nLine2"
echo -e "Tab\tSeparated"
# 输出到文件
echo "content" > file.txt
echo "content" >> file.txt
# 输出变量
name="John"
echo "Hello, $name"
echo 'Hello, $name' # 单引号不解析变量
3.2 printf 格式化输出 #
bash
# 格式化输出
printf "Name: %s\n" "John"
printf "Age: %d\n" 25
printf "Price: %.2f\n" 99.99
# 格式说明符
%s # 字符串
%d # 整数
%f # 浮点数
%x # 十六进制
%o # 八进制
%e # 科学计数法
# 宽度和对齐
printf "%10s\n" "test" # 右对齐,宽度 10
printf "%-10s\n" "test" # 左对齐,宽度 10
printf "%.2f\n" 99.999 # 保留 2 位小数
3.3 read 输入 #
bash
# 基本输入
read name
echo "Hello, $name"
# 带提示
read -p "Enter your name: " name
# 静默输入(密码)
read -s -p "Enter password: " password
# 限制输入长度
read -n 1 -p "Continue? [y/n] " answer
# 超时
read -t 10 -p "Enter name (10s): " name
# 读取到数组
read -a arr -p "Enter numbers: "
echo ${arr[@]}
# 读取多行
while read line; do
echo "Line: $line"
done < file.txt
四、运算 #
4.1 算术运算 #
bash
# 使用 $(( ))
a=10
b=3
echo $((a + b)) # 加
echo $((a - b)) # 减
echo $((a * b)) # 乘
echo $((a / b)) # 除
echo $((a % b)) # 取余
echo $((a ** b)) # 幂
# 使用 expr
expr 10 + 3
expr 10 - 3
expr 10 \* 3 # * 需要转义
expr 10 / 3
expr 10 % 3
# 使用 let
let a=10+3
let a++
let a--
# 使用 bc(支持浮点)
echo "10.5 + 3.2" | bc
echo "scale=2; 10/3" | bc
4.2 比较运算 #
bash
# 数字比较
-eq # 等于
-ne # 不等于
-gt # 大于
-lt # 小于
-ge # 大于等于
-le # 小于等于
# 示例
[ $a -eq $b ]
[[ $a -eq $b ]]
test $a -eq $b
4.3 字符串比较 #
bash
# 字符串比较
= # 等于
!= # 不等于
-z # 长度为零
-n # 长度非零
# 示例
[ "$str1" = "$str2" ]
[[ "$str1" == "$str2" ]]
[ -z "$str" ]
[ -n "$str" ]
4.4 文件测试 #
bash
# 文件测试
-e # 文件存在
-f # 是普通文件
-d # 是目录
-r # 可读
-w # 可写
-x # 可执行
-s # 文件非空
-L # 是符号链接
# 示例
[ -f file.txt ]
[ -d directory ]
[ -x script.sh ]
[ -r file.txt ] && [ -w file.txt ]
五、流程控制 #
5.1 if 条件 #
bash
# 基本 if
if [ condition ]; then
commands
fi
# if-else
if [ condition ]; then
commands1
else
commands2
fi
# if-elif-else
if [ condition1 ]; then
commands1
elif [ condition2 ]; then
commands2
else
commands3
fi
# 示例
if [ $age -ge 18 ]; then
echo "Adult"
else
echo "Minor"
fi
# 使用 [[ ]](推荐)
if [[ $str == "hello" ]]; then
echo "Match"
fi
# 逻辑运算
if [ $a -gt 0 ] && [ $a -lt 10 ]; then
echo "0 < a < 10"
fi
if [ $a -lt 0 ] || [ $a -gt 10 ]; then
echo "a < 0 or a > 10"
fi
5.2 case 分支 #
bash
# case 语句
case $variable in
pattern1)
commands1
;;
pattern2)
commands2
;;
*)
default_commands
;;
esac
# 示例
case $1 in
start)
echo "Starting..."
;;
stop)
echo "Stopping..."
;;
restart)
echo "Restarting..."
;;
*)
echo "Usage: $0 {start|stop|restart}"
;;
esac
# 模式匹配
case $answer in
[yY]|[yY][eE][sS])
echo "Yes"
;;
[nN]|[nN][oO])
echo "No"
;;
*)
echo "Unknown"
;;
esac
5.3 for 循环 #
bash
# 列表循环
for item in item1 item2 item3; do
echo $item
done
# 遍历文件
for file in *.txt; do
echo $file
done
# 遍历命令输出
for user in $(cat /etc/passwd | cut -d: -f1); do
echo $user
done
# C 风格循环
for ((i=0; i<10; i++)); do
echo $i
done
# 遍历数组
arr=(1 2 3 4 5)
for num in ${arr[@]}; do
echo $num
done
# 遍历参数
for arg in "$@"; do
echo $arg
done
5.4 while 循环 #
bash
# 基本 while
while [ condition ]; do
commands
done
# 示例
count=0
while [ $count -lt 10 ]; do
echo $count
((count++))
done
# 读取文件
while read line; do
echo $line
done < file.txt
# 无限循环
while true; do
echo "Running..."
sleep 1
done
# 按条件退出
while true; do
read -p "Continue? (y/n) " answer
[[ $answer == "n" ]] && break
done
5.5 until 循环 #
bash
# until 循环(条件为假时执行)
until [ condition ]; do
commands
done
# 示例
count=0
until [ $count -ge 10 ]; do
echo $count
((count++))
done
5.6 循环控制 #
bash
# break - 退出循环
for i in {1..10}; do
if [ $i -eq 5 ]; then
break
fi
echo $i
done
# continue - 跳过本次循环
for i in {1..10}; do
if [ $i -eq 5 ]; then
continue
fi
echo $i
done
# 嵌套循环
for i in {1..3}; do
for j in {1..3}; do
echo "$i, $j"
done
done
六、函数 #
6.1 函数定义 #
bash
# 方式一
function_name() {
commands
return value
}
# 方式二
function function_name {
commands
return value
}
# 调用函数
function_name
function_name arg1 arg2
6.2 函数参数 #
bash
# 函数参数
greet() {
echo "Hello, $1"
echo "Age: $2"
}
greet "John" 25
# 所有参数
show_args() {
echo "Arguments: $@"
echo "Count: $#"
}
show_args a b c d
6.3 返回值 #
bash
# 使用 return
add() {
return $(($1 + $2))
}
add 10 20
echo $? # 返回值
# 使用 echo
add() {
echo $(($1 + $2))
}
result=$(add 10 20)
echo $result
6.4 局部变量 #
bash
# 使用 local
func() {
local var="local"
echo $var
}
var="global"
func
echo $var # 输出 global
6.5 递归函数 #
bash
# 阶乘
factorial() {
if [ $1 -le 1 ]; then
echo 1
else
local prev=$(factorial $(($1 - 1)))
echo $(($1 * prev))
fi
}
result=$(factorial 5)
echo $result # 输出 120
七、实战脚本示例 #
7.1 系统备份脚本 #
bash
#!/bin/bash
# 系统备份脚本
BACKUP_DIR="/backup"
DATE=$(date +%Y%m%d)
BACKUP_FILE="backup_$DATE.tar.gz"
# 创建备份目录
mkdir -p $BACKUP_DIR
# 备份
tar -czf $BACKUP_DIR/$BACKUP_FILE /home /etc
# 删除 7 天前的备份
find $BACKUP_DIR -name "backup_*.tar.gz" -mtime +7 -delete
echo "Backup completed: $BACKUP_FILE"
7.2 日志分析脚本 #
bash
#!/bin/bash
# 日志分析脚本
LOG_FILE="/var/log/nginx/access.log"
echo "=== 访问量 Top 10 IP ==="
awk '{print $1}' $LOG_FILE | sort | uniq -c | sort -rn | head -10
echo "=== 访问量 Top 10 URL ==="
awk '{print $7}' $LOG_FILE | sort | uniq -c | sort -rn | head -10
echo "=== HTTP 状态码统计 ==="
awk '{print $9}' $LOG_FILE | sort | uniq -c | sort -rn
7.3 服务监控脚本 #
bash
#!/bin/bash
# 服务监控脚本
SERVICES=("nginx" "mysql" "redis")
for service in ${SERVICES[@]}; do
if systemctl is-active --quiet $service; then
echo "$service is running"
else
echo "$service is stopped"
systemctl start $service
echo "$service has been started"
fi
done
7.4 批量用户创建脚本 #
bash
#!/bin/bash
# 批量创建用户
USER_FILE="users.txt"
if [ ! -f $USER_FILE ]; then
echo "File $USER_FILE not found"
exit 1
fi
while read username; do
if id $username &>/dev/null; then
echo "User $username already exists"
else
useradd -m -s /bin/bash $username
echo "$username:password" | chpasswd
echo "User $username created"
fi
done < $USER_FILE
八、小结 #
本章学习了 Shell 脚本编程的基础知识,包括变量、流程控制、函数和实战脚本。
关键要点:
- Shebang 指定脚本解释器
- 变量无需声明,直接赋值
- 使用
[[ ]]进行条件测试 - 函数使用
local定义局部变量 - 脚本要有良好的注释和错误处理
下一章预告: 系统监控 - 学习系统性能监控和资源管理。
最后更新:2026-03-27