基础语法
Shebang 和执行
#!/bin/bash
# 或
#!/usr/bin/env bash
# 执行方式
chmod +x script.sh
./script.sh
# 或直接执行
bash script.sh
变量
# 赋值(等号两边不能有空格)
name="John"
age=25
readonly PI=3.14 # 只读变量
# 使用变量
echo "Name: $name"
echo "Name: ${name}"
echo "Length: ${#name}" # 字符串长度
# 默认值
${var:-default} # var 未定义或为空时使用 default
${var:=default} # var 未定义或为空时赋值为 default
${var:+value} # var 已定义且非空时使用 value
${var:?error msg} # var 未定义或为空时报错
# 字符串操作
${string:offset:length} # 子串
${string#pattern} # 删除左边最短匹配
${string##pattern} # 删除左边最长匹配
${string%pattern} # 删除右边最短匹配
${string%%pattern} # 删除右边最长匹配
${string/pattern/replacement} # 替换
数组
# 索引数组
arr=(apple banana cherry)
arr[0]="apple"
arr[1]="banana"
# 访问
echo ${arr[0]} # 第一个元素
echo ${arr[@]} # 所有元素
echo ${#arr[@]} # 数组长度
echo ${arr[@]:1:2} # 从索引1开始取2个
# 关联数组
declare -A map
map[name]="John"
map[age]=25
echo ${map[name]}
echo ${!map[@]} # 所有键
echo ${map[@]} # 所有值
流程控制
条件判断
# if 语句
if [ condition ]; then
# commands
elif [ condition ]; then
# commands
else
# commands
fi
# 常用条件
[ -f file ] # 文件存在且是普通文件
[ -d dir ] # 目录存在
[ -r file ] # 文件可读
[ -w file ] # 文件可写
[ -x file ] # 文件可执行
[ -s file ] # 文件非空
[ -z string ] # 字符串为空
[ -n string ] # 字符串非空
[ "$a" = "$b" ] # 字符串相等
[ "$a" != "$b" ] # 字符串不相等
[ $a -eq $b ] # 数值相等
[ $a -gt $b ] # 数值大于
[ $a -lt $b ] # 数值小于
[ $a -ge $b ] # 数值大于等于
[ $a -le $b ] # 数值小于等于
# [[ ]] 增强版(支持正则)
[[ $string =~ pattern ]] # 正则匹配
[[ $a == $b ]] # 字符串比较
[[ $a -gt $b && $c -lt $d ]] # 逻辑与
# case 语句
case $var in
pattern1)
# commands
;;
pattern2)
# commands
;;
*)
# default
;;
esac
循环
# for 循环
for i in 1 2 3 4 5; do
echo $i
done
# C 风格 for
for ((i=0; i<10; i++)); do
echo $i
done
# 范围
for i in {1..10}; do
echo $i
done
# 带步长
for i in {0..100..5}; do
echo $i
done
# 遍历文件
for file in /var/log/*.log; do
echo "Processing: $file"
done
# while 循环
while [ condition ]; do
# commands
done
# 读取文件
while IFS= read -r line; do
echo "Line: $line"
done < file.txt
# 无限循环
while true; do
# commands
sleep 1
done
# until 循环(条件为假时执行)
until [ condition ]; do
# commands
done
# break 和 continue
for i in {1..10}; do
if [ $i -eq 5 ]; then
continue # 跳过本次
fi
if [ $i -eq 8 ]; then
break # 跳出循环
fi
echo $i
done
函数
# 定义函数
function greet() {
echo "Hello, $1!"
}
# 或简写
greet() {
echo "Hello, $1!"
}
# 调用
greet "John"
# 返回值
add() {
echo $(( $1 + $2 ))
}
result=$(add 3 5)
# 返回状态码
check_file() {
if [ -f "$1" ]; then
return 0 # 成功
else
return 1 # 失败
fi
}
if check_file "/etc/passwd"; then
echo "File exists"
fi
# 局部变量
myfunc() {
local var="local"
echo $var
}
文本处理
grep
# 基本搜索
grep "pattern" file.txt
# 选项
-i # 忽略大小写
-r # 递归搜索
-n # 显示行号
-c # 只显示匹配数量
-l # 只显示文件名
-v # 反向匹配
-w # 全词匹配
-e # 多个模式
-E # 扩展正则
-P # Perl 正则
# 示例
grep -rn "error" /var/log/
grep -i "warning" *.log
grep -E "^[0-9]{4}-[0-9]{2}" log.txt
sed
# 替换
sed 's/old/new/' file.txt
sed 's/old/new/g' file.txt # 全局替换
sed -i 's/old/new/g' file.txt # 原地修改
# 删除
sed '/pattern/d' file.txt
sed '1,5d' file.txt # 删除1-5行
# 插入
sed '3i\new line' file.txt # 在第3行前插入
sed '3a\new line' file.txt # 在第3行后插入
# 打印
sed -n '1,5p' file.txt # 打印1-5行
sed -n '/pattern/p' file.txt # 打印匹配行
# 常用示例
sed 's/[[:space:]]*$//' file.txt # 删除行尾空格
sed '/^$/d' file.txt # 删除空行
sed 's/^[[:space:]]*//' file.txt # 删除行首空格
awk
# 基本语法
awk '{print $1, $3}' file.txt # 打印第1和第3列
# 条件过滤
awk '$3 > 100' file.txt
awk '/pattern/ {print $1}' file.txt
# 内置变量
NR # 当前行号
NF # 当前行字段数
$0 # 整行
$1-$n # 第1到第n个字段
# BEGIN/END
awk 'BEGIN {print "Start"} {print $1} END {print "End"}' file.txt
# 计算
awk '{sum += $1} END {print sum}' file.txt
awk '{avg += $1} END {print avg/NR}' file.txt
# 格式化输出
awk '{printf "%-20s %10d\n", $1, $2}' file.txt
# 字段分隔符
awk -F: '{print $1, $3}' /etc/passwd
awk -F',' '{print $1}' data.csv
cut
# 按字符
cut -c1-5 file.txt
# 按字段
cut -d: -f1,3 /etc/passwd
# 按字节
cut -b1-10 file.txt
sort
sort file.txt # 字母排序
sort -n file.txt # 数值排序
sort -r file.txt # 逆序
sort -k2 -t: file.txt # 按第2列排序,分隔符为:
sort -u file.txt # 去重
sort -h file.txt # 人类可读排序(1K, 2M, 3G)
uniq
sort file.txt | uniq # 去重
sort file.txt | uniq -c # 计数
sort file.txt | uniq -d # 只显示重复行
sort file.txt | uniq -u # 只显示唯一行
高级特性
进程替换
# 比较两个命令的输出
diff <(ls /dir1) <(ls /dir2)
# 同时读取多个文件
while read line1 <&3 && read line2 <&4; do
echo "File1: $line1, File2: $line2"
done 3< file1.txt 4< file2.txt
陷阱处理
# 清理临时文件
cleanup() {
rm -f "$tmpfile"
echo "Cleaned up"
}
trap cleanup EXIT
# 忽略信号
trap '' SIGINT SIGTERM
# 捕获错误
trap 'echo "Error on line $LINENO"' ERR
Here Document
# 基本用法
cat << EOF
This is a heredoc
Variable: $name
EOF
# 不解析变量
cat << 'EOF'
This is a heredoc
Variable: $name will not be expanded
EOF
# 缩进
cat <<- EOF
This heredoc allows tabs for indentation
EOF
子 Shell
# 在子 Shell 中执行
(
cd /tmp
ls -la
)
# 当前目录不变
# 命令组
{
echo "Line 1"
echo "Line 2"
} > output.txt
实战案例
系统监控脚本
#!/bin/bash
LOG_FILE="/var/log/monitor.log"
log() {
echo "$(date '+%Y-%m-%d %H:%M:%S') $1" >> "$LOG_FILE"
}
check_disk() {
usage=$(df -h / | awk 'NR==2{print $5}' | tr -d '%')
if [ "$usage" -gt 80 ]; then
log "WARNING: Disk usage is ${usage}%"
fi
}
check_memory() {
free -m | awk 'NR==2{printf "Memory: %.1f%%\n", $3*100/$2}'
}
check_load() {
load=$(uptime | awk -F'load average:' '{print $2}' | awk -F, '{print $1}')
if [ $(echo "$load > 2.0" | bc) -eq 1 ]; then
log "WARNING: Load average is $load"
fi
}
while true; do
check_disk
check_memory
check_load
sleep 60
done
自动备份脚本
#!/bin/bash
BACKUP_DIR="/backup"
SOURCE_DIR="/var/www"
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_FILE="$BACKUP_DIR/backup_$DATE.tar.gz"
mkdir -p "$BACKUP_DIR"
tar -czf "$BACKUP_FILE" "$SOURCE_DIR"
# 保留最近7天的备份
find "$BACKUP_DIR" -name "backup_*.tar.gz" -mtime +7 -delete
echo "Backup completed: $BACKUP_FILE"
批量重命名
#!/bin/bash
# 批量添加前缀
for file in *.txt; do
mv "$file" "prefix_$file"
done
# 批量修改扩展名
for file in *.jpeg; do
mv "$file" "${file%.jpeg}.jpg"
done
# 批量重命名为序号
i=1
for file in *.jpg; do
mv "$file" "$(printf '%03d' $i).jpg"
((i++))
done
日志分析
#!/bin/bash
LOG_FILE="/var/log/access.log"
echo "=== 访问统计 ==="
awk '{print $1}' "$LOG_FILE" | sort | uniq -c | sort -rn | head -10
echo ""
echo "=== 错误统计 ==="
grep -c "500\|502\|503" "$LOG_FILE"
echo ""
echo "=== 每小时访问量 ==="
awk '{print $4}' "$LOG_FILE" | cut -d: -f2 | sort | uniq -c | sort -n
面试常见问题
$@和$*的区别?$@: 每个参数作为独立字符串$*: 所有参数作为一个字符串- 建议始终使用
"$@"
[[ ]]和[ ]的区别?[[ ]]是 bash 增强版- 支持正则、模式匹配
- 更安全,不需要引号
如何捕获命令输出?
var=$(command): 捕获标准输出var=$(command 2>&1): 捕获所有输出command | var: 管道捕获
如何调试 bash 脚本?
bash -x script.sh: 显示执行过程set -x: 在脚本中开启调试set -e: 遇到错误立即退出set -u: 使用未定义变量时报错