13
10
月
2026
每天要敲的命令、每周要做的维护、每次部署要走的步骤——这些都该写成脚本。写一次,以后一条命令搞定,还不会漏步骤。
这篇从零讲 Bash 脚本:变量、条件、循环、函数、参数处理,以及能直接用的实战模板。
脚本需要一台能跑的服务器来练。财富云计算 提供云服务器;内网服务、测试环境可选 NAT 型(无独立公网 IP,价格比带独立 IP 机型省一半以上)。
一、第一个脚本
nano hello.sh#!/bin/bash
echo "Hello, World!"chmod +x hello.sh
./hello.sh关键点:
- 第一行
#!/bin/bash是 shebang,指定解释器,必须有 chmod +x加执行权限./hello.sh执行(不能只写hello.sh,除非在 PATH 里)
二、变量
#!/bin/bash
# 定义变量(等号两边不能有空格!)
NAME="服务器"
COUNT=10
# 使用变量
echo "你好,$NAME"
echo "数量: ${COUNT}"
# 命令结果赋值给变量
DATE=$(date +%F)
FILES=$(ls /var/www)
TOTAL=`wc -l < file.txt` # 反引号(老写法)
# 只读变量
readonly PI=3.14
# 删除变量
unset NAME常见错误:NAME = "x"(等号两边有空格会报错)
特殊变量
| 变量 | 含义 |
|---|---|
$0 | 脚本名 |
$1~$9 | 第 1~9 个参数 |
$# | 参数个数 |
$@ | 所有参数(每个独立) |
$* | 所有参数(作为一个字符串) |
$? | 上一条命令的退出码(0=成功) |
$$ | 当前进程 PID |
$! | 最后一个后台进程 PID |
#!/bin/bash
echo "脚本名: $0"
echo "第一个参数: $1"
echo "参数个数: $#"
echo "所有参数: $@"./script.sh a b c
# 脚本名: ./script.sh
# 第一个参数: a
# 参数个数: 3
# 所有参数: a b c环境变量
export PATH="/usr/local/bin:$PATH"
export LANG=en_US.UTF-8
echo $HOME
echo $USER
echo $PWD三、输入与输出
# 输出
echo "文本"
echo -n "不换行"
echo -e "支持\t转义\n字符"
# 带颜色
RED='\033[0;31m'
GREEN='\033[0;32m'
NC='\033[0m' # No Color
echo -e "${GREEN}成功${NC}"
echo -e "${RED}错误${NC}"
# 读取输入
read -p "请输入: " INPUT
echo "你输入了: $INPUT"
# 静默输入(密码)
read -s -p "密码: " PASSWD四、条件判断
if 语句
if [ 条件 ]; then
命令
elif [ 条件 ]; then
命令
else
命令
fi文件判断
| 表达式 | 含义 |
|---|---|
-f file | 是普通文件 |
-d dir | 是目录 |
-e path | 存在 |
-r file | 可读 |
-w file | 可写 |
-x file | 可执行 |
-s file | 文件大小 > 0 |
-z str | 字符串为空 |
-n str | 字符串非空 |
if [ -f "/etc/nginx/nginx.conf" ]; then
echo "配置文件存在"
fi
if [ ! -d "/backup" ]; then
mkdir -p /backup
fi字符串比较
if [ "$NAME" = "root" ]; then # 相等(注意空格)
if [ "$NAME" != "root" ]; then # 不等
if [ -z "$INPUT" ]; then # 为空数值比较
if [ $COUNT -eq 10 ]; then # 等于
if [ $COUNT -ne 10 ]; then # 不等于
if [ $COUNT -gt 10 ]; then # 大于
if [ $COUNT -ge 10 ]; then # 大于等于
if [ $COUNT -lt 10 ]; then # 小于
if [ $COUNT -le 10 ]; then # 小于等于逻辑运算
if [ -f "$F" ] && [ -r "$F" ]; then # 与
if [ "$A" = "x" ] || [ "$B" = "y" ]; then # 或
if ! [ -f "$F" ]; then # 非
# 或用双括号(更强大,推荐)
if [[ -f "$F" && -r "$F" ]]; then
if [[ "$STR" == a* ]]; then # 支持通配符
if [[ "$STR" =~ ^[0-9]+$ ]]; then # 支持正则case 语句
case "$1" in
start)
echo "启动"
;;
stop)
echo "停止"
;;
restart)
echo "重启"
;;
*)
echo "用法: $0 {start|stop|restart}"
exit 1
;;
esac五、循环
for 循环
# 遍历列表
for i in 1 2 3 4 5; do
echo $i
done
# 范围
for i in {1..10}; do
echo $i
done
# 遍历文件
for file in /var/log/*.log; do
echo "处理 $file"
done
# C 风格
for ((i=0; i<10; i++)); do
echo $i
done
# 遍历命令输出
for user in $(cat users.txt); do
echo $user
donewhile 循环
# 计数
COUNT=0
while [ $COUNT -lt 5 ]; do
echo $COUNT
COUNT=$((COUNT + 1))
done
# 读文件每一行
while read line; do
echo "$line"
done < file.txt
# 无限循环
while true; do
echo "每 5 秒执行一次"
sleep 5
done循环控制
break # 跳出循环
continue # 跳过本次六、函数
# 定义
function greet() {
echo "你好,$1"
}
# 或(推荐写法)
greet() {
local name=$1 # local 定义局部变量
echo "你好,$name"
return 0
}
# 调用
greet "张三"
# 返回值(0-255,用 $? 获取)
greet "李四"
echo "返回码: $?"
# 返回值(字符串,用 echo 输出 + 命令替换)
get_date() {
echo $(date +%F)
}
TODAY=$(get_date)七、错误处理(重要)
#!/bin/bash
set -e # 任何命令失败就退出脚本
set -u # 使用未定义变量就报错
set -o pipefail # 管道中任一命令失败就失败
set -euo pipefail # 三个一起用(推荐)
# 或手动检查
if ! command -v nginx > /dev/null; then
echo "错误: nginx 未安装"
exit 1
fi
# 检查上一条命令
cp a.txt b.txt
if [ $? -ne 0 ]; then
echo "复制失败"
exit 1
fi
# 或者
cp a.txt b.txt || { echo "复制失败"; exit 1; }清理工作用 trap:
#!/bin/bash
TEMP=$(mktemp)
trap "rm -f $TEMP" EXIT # 脚本退出时(无论正常还是出错)清理
echo "工作中..." > $TEMP
# 中间即使出错退出,临时文件也会被删八、实用技巧
1)命令替换与算术
# 算术运算
echo $((1 + 2))
echo $((COUNT * 2))
let "COUNT++"2)字符串处理
STR="hello world"
echo ${#STR} # 长度:11
echo ${STR:0:5} # 截取:hello
echo ${STR/world/bash} # 替换:hello bash
echo ${STR^^} # 转大写
echo ${STR^^} # 转大写(Bash 4+)3)检查命令是否存在
command -v nginx > /dev/null 2>&1 && echo "已安装" || echo "未安装"4)检查是否为 root
if [ "$(id -u)" != "0" ]; then
echo "请用 root 运行"
exit 1
fi5)日志输出函数
log() {
echo "[$(date '+%F %T')] $*" | tee -a /var/log/myscript.log
}
log "开始备份"
log "备份完成"6)调试
bash -x script.sh # 显示每条命令执行过程
# 或在脚本里
set -x # 开启调试
# 某段代码
set +x # 关闭调试7)检查语法
bash -n script.sh # 只检查语法,不执行建议装 shellcheck(更专业的检查工具):
apt install shellcheck -y
shellcheck script.sh九、实战模板
模板一:备份脚本
#!/bin/bash
set -euo pipefail
BACKUP_DIR="/backup"
SRC_DIR="/var/www/html"
DB_NAME="mydb"
DB_USER="root"
KEEP_DAYS=30
log() {
echo "[$(date '+%F %T')] $*"
}
# 检查目录
mkdir -p "$BACKUP_DIR"
DATE=$(date +%F)
# 备份数据库
log "开始备份数据库..."
mysqldump -u"$DB_USER" --single-transaction "$DB_NAME" \
| gzip > "$BACKUP_DIR/db-$DATE.sql.gz"
# 备份文件
log "开始备份文件..."
tar -czf "$BACKUP_DIR/files-$DATE.tar.gz" "$SRC_DIR" 2>/dev/null
# 清理旧备份
log "清理 ${KEEP_DAYS} 天前的备份..."
find "$BACKUP_DIR" -name "*.tar.gz" -mtime +$KEEP_DAYS -delete
find "$BACKUP_DIR" -name "*.sql.gz" -mtime +$KEEP_DAYS -delete
# 检查大小
SIZE=$(du -sh "$BACKUP_DIR" | cut -f1)
log "备份完成,总大小: $SIZE"模板二:服务管理脚本
#!/bin/bash
set -euo pipefail
SERVICE="nginx"
case "${1:-}" in
start)
systemctl start $SERVICE
;;
stop)
systemctl stop $SERVICE
;;
restart)
systemctl restart $SERVICE
;;
status)
systemctl status $SERVICE
;;
*)
echo "用法: $0 {start|stop|restart|status}"
exit 1
;;
esac模板三:健康检查脚本
#!/bin/bash
URL="https://example.com"
EXPECT=200
CODE=$(curl -s -o /dev/null -w "%{http_code}" --max-time 10 "$URL" || echo "000")
if [ "$CODE" != "$EXPECT" ]; then
echo "[$(date)] 异常: $URL 返回 $CODE" >> /var/log/health.log
# 可选:自动重启服务
# systemctl restart nginx
exit 1
fi
echo "[$(date)] 正常" >> /var/log/health.log模板四:批量处理脚本
#!/bin/bash
set -euo pipefail
# 批量压缩目录下所有 jpg
for img in /var/www/html/uploads/*.jpg; do
[ -e "$img" ] || continue
echo "处理: $img"
jpegoptim --max=85 "$img"
done
echo "全部处理完成"模板五:带参数的部署脚本
#!/bin/bash
set -euo pipefail
usage() {
echo "用法: $0 -e <环境> [-b <分支>]"
echo " -e 环境: prod | test"
echo " -b 分支,默认 main"
exit 1
}
ENV=""
BRANCH="main"
while getopts "e:b:" opt; do
case $opt in
e) ENV=$OPTARG ;;
b) BRANCH=$OPTARG ;;
*) usage ;;
esac
done
if [ -z "$ENV" ]; then
usage
fi
echo "部署环境: $ENV"
echo "分支: $BRANCH"
case $ENV in
prod)
DIR="/var/www/prod"
;;
test)
DIR="/var/www/test"
;;
*)
echo "未知环境: $ENV"
exit 1
;;
esac
cd "$DIR"
git pull origin "$BRANCH"
echo "部署完成"十、脚本放哪、怎么定时
# 个人脚本
~/bin/
# 系统级脚本
/usr/local/bin/
# 加执行权限并可直接调用
chmod +x /usr/local/bin/myscript.sh
myscript.sh定时执行:
crontab -e
0 3 * * * /usr/local/bin/backup.sh >> /var/log/backup.log 2>&1详见《crontab 计划任务完全指南》。
十一、常见错误
| 错误 | 原因 | 解决 |
|---|---|---|
Permission denied | 没执行权限 | chmod +x |
command not found(手动能跑) | cron 环境 PATH 不同 | 用绝对路径 |
syntax error near unexpected token | 括号/引号不匹配 | bash -n 检查 |
| 变量为空 | 等号两边有空格,或没 $ | 检查语法 |
[ : integer expression expected | 数值比较用了字符串 | 确认是数字 |
| 脚本执行一半退出 | 遇到错误 | 加 set -e 明确行为 |
| 中文乱码 | 编码问题 | export LANG=en_US.UTF-8 |
| Windows 编辑的脚本报错 | CRLF 换行符 | dos2unix script.sh |
bad interpreter | shebang 路径错 | 改成 #!/bin/bash 或用 #!/usr/bin/env bash |
十二、进阶方向
掌握基础后可以学:
- awk / sed:强大的文本处理,见《sed 与 awk 文本处理实战》
- 正则表达式:配合 grep/sed/awk
- expect:自动交互(自动输入密码等)
- 并行处理:
xargs -P、GNU parallel - 单元测试:bats(Bash 测试框架)
写好脚本能省下大量重复劳动。财富云计算 的云服务器提供完整 root 权限,可自由部署脚本与定时任务;内网服务、测试环境可选 NAT 型(无独立公网 IP,价格比带独立 IP 机型省一半以上),适合长期挂自动化任务。
相关阅读:
- 《crontab 计划任务完全指南》
- 《Linux 常用命令速查表》
- 《服务器备份与快照策略》
延伸阅读
- 网站环境怎么选:LNMP 手动 / 宝塔面板 / Docker / 云开发
- 问卷表单系统搭建:SurveyKing / Formbricks,数据自己收
- 图床搭建:Lsky Pro / Lychee,自托管图片托管方案
- 邮件列表搭建:listmonk 自托管 Newsletter,把读者握在自己手里
- 文档站点搭建:MkDocs / VuePress / Docsify,产品文档与技术手册方案
- RSS 阅读器搭建:FreshRSS 自托管,把信息流拿回自己手里
- 短链接服务搭建:Shlink 自托管,链接数据自己掌握
- 网站状态监控页搭建:Uptime Kuma,服务器挂了第一时间知道

[...]netsh advfirewall firewall add rule name="GameRange" dir=in action=allow protocol=UDP localport=26901-26903完整的 Windows 环境准备(远程桌面连接、运行库安装、SteamCMD 用法、开机自启、8 个常见坑)见:《Windows 云服务器开服通用前置教程》→ htt[...]
2026-10-18 回复