Shell脚本如何顺序执行依赖任务:从基础到高级的完整指南
目录导读
- 什么是依赖任务与顺序执行?
- Shell脚本顺序执行的基础方法
- 处理任务依赖的常见场景
- 高级技巧:并行中的顺序控制
- 实战案例:部署脚本中的依赖链
- 常见问题与解决方案(问答)
什么是依赖任务与顺序执行?
在实际的运维或开发工作中,我们经常遇到这样的场景:任务B必须等任务A完成后才能执行,任务C又依赖任务B的结果,这种任务间的前置关系叫做“依赖任务”,Shell脚本是处理这类场景最直接的工具,它通过简单的命令排列、条件判断和循环控制,就能实现精准的顺序执行。

编译代码必须先下载依赖包,部署服务必须先停止旧进程,忽略依赖顺序会导致脚本崩溃或产生不可预测的后果。
Shell脚本顺序执行的基础方法
1 使用分号和换行符
最简单的顺序执行就是直接将命令按顺序写在一起:
echo "步骤1:检查环境" check_env.sh echo "步骤2:下载依赖" download_deps.sh echo "步骤3:编译项目" build.sh
Shell会从上到下依次执行每行命令,但这种方式无法处理前一步失败的情况。
2 使用逻辑控制符
利用&&和可以实现有条件的顺序执行:
check_env.sh && download_deps.sh && build.sh
只有前一个命令成功(退出码为0),才会执行下一个,如果失败,后续步骤自动跳过。
3 使用if条件判断
更清晰的依赖处理方式:
if check_env.sh; then
echo "环境检查通过"
if download_deps.sh; then
build.sh
else
echo "下载依赖失败,终止"
exit 1
fi
else
echo "环境检查失败"
exit 1
fi
处理任务依赖的常见场景
顺序无依赖
多个独立任务,但要求按顺序逐个执行:
for task in "初始化配置" "安装包" "设置权限"; do
echo "执行:$task"
eval "${task// /_}.sh" || exit 1
done
线性依赖链
任务A→任务B→任务C,每个必须成功:
function run_chain() {
task_a || return 1
task_b || return 1
task_c || return 1
}
部分依赖(DAG结构)
任务C依赖A和B,但A和B可以并行执行,这里先用顺序方式处理:
task_a
task_b
# 确保两者都完成后
if [ $? -eq 0 ]; then
task_c
fi
高级技巧:并行中的顺序控制
当需要提升效率时,可以使用后台执行与wait结合:
task_a & # 后台运行 task_b & # 后台运行 wait # 等待所有后台进程完成 # 此时A和B都已完成 task_c # 开始依赖任务
如果需要更精细的控制,可以使用临时文件标记完成状态:
# 任务A完成时创建标记文件
task_a && touch /tmp/done_a &
task_b && touch /tmp/done_b &
# 等待两个标记文件出现
while [ ! -f /tmp/done_a ] || [ ! -f /tmp/done_b ]; do
sleep 1
done
task_c
实战案例:部署脚本中的依赖链
假设我们要部署一个Node.js应用,依赖顺序为:
- 安装系统依赖(git,nvm)
- 克隆仓库
- 安装npm包
- 运行测试
- 启动服务
完整脚本示例:
#!/bin/bash
set -e # 任何错误立即退出
DEPLOY_DIR="/var/www/app"
LOG_FILE="/var/log/deploy.log"
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a $LOG_FILE
}
log "开始部署流程"
# 步骤1:系统准备
if command -v git &>/dev/null && command -v node &>/dev/null; then
log "系统环境已就绪"
else
log "安装系统依赖..."
apt-get update && apt-get install -y git curl
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash
fi
# 步骤2:克隆(依赖步骤1)
if [ ! -d "$DEPLOY_DIR" ]; then
log "克隆仓库..."
git clone https://example.com/repo.git $DEPLOY_DIR
else
log "拉取最新代码..."
cd $DEPLOY_DIR && git pull
fi
# 步骤3:安装npm(依赖步骤2)
cd $DEPLOY_DIR
log "安装npm依赖..."
npm ci
# 步骤4:测试(依赖步骤3)
log "运行测试..."
npm test
# 步骤5:启动(依赖步骤4)
log "启动服务..."
pm2 restart app.js 2>/dev/null || pm2 start app.js
log "部署完成"
常见问题与解决方案(问答)
问:如果依赖任务执行失败,如何做重试机制?
答:使用循环与计数器。
MAX_RETRIES=3
RETRY_COUNT=0
until [ $RETRY_COUNT -ge $MAX_RETRIES ]; do
dangerous_task && break
RETRY_COUNT=$((RETRY_COUNT+1))
echo "重试 $RETRY_COUNT/$MAX_RETRIES"
sleep 5
done
if [ $RETRY_COUNT -ge $MAX_RETRIES ]; then
echo "任务失败,已重试$MAX_RETRIES次"
exit 1
fi
问:如何确保脚本中某些步骤必须按顺序,而其他步骤可以并行?
答:使用wait控制点,如上述高级技巧所示,或者用flock文件锁实现跨进程同步:
# 步骤A和B并行,步骤C等待两者 (step_a && touch /tmp/step_a.done) & (step_b && touch /tmp/step_b.done) & wait step_c # 此时必等A和B完成
问:Shell脚本中如何判断依赖是否已满足?
答:检查前置任务生成的文件、进程状态或返回值,常用方法:
- 检查文件是否存在:
[ -f /tmp/flag ] - 检查进程运行:
pgrep -x "process_name" - 检查退出码:
command || echo "失败"
问:顺序执行时,如果某一任务耗时很久,如何显示进度?
答:在长时间任务中加入进度指示:
long_task &
PID=$!
while kill -0 $PID 2>/dev/null; do
echo -n "."
sleep 1
done
echo "完成"
更专业的工具是pv或progress命令,但需要额外安装。