You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让PHP触发的Bash脚本等待npm构建完成后再执行?

解决PHP触发bash脚本时npm命令未完成就执行rsync的问题

问题场景

搭建React个人网站自动化部署流程,基于Vite打包,流程为:本地推送代码至远程仓库 → Webhook触发服务器PHP脚本 → PHP调用bash脚本依次执行git拉取、npm install、npm run build、rsync同步构建产物到线上目录。
手动通过PuTTY运行bash脚本完全正常,但PHP调用时,bash脚本未等待npm install和build完成就执行rsync,导致同步的是上一次的构建结果。尝试过添加pause 120和&&链式调用,手动运行有效但PHP调用时无效。

相关代码

PHP调用bash的代码:

$commandOutput = shell_exec("/bin/bash ./sync.sh --repo=" . $hook["repo"] . " --branch=" . $hook["branch"] . " --subdomain=" . $subdomain);

bash脚本核心代码:

if [[ ${repo} && ${branch} && ${subdomain} ]]; then
  git -C /path/"${repo}"/ checkout "${branch}"
  git -C /path/"${repo}"/ fetch
  git -C /path/"${repo}"/ pull origin "${branch}"
  npm --prefix /path/"${repo}"/ install
  npm --prefix /path/"${repo}"/ run build;
  rsync -auv /path/"${repo}"/htdocs/* /path/"${subdomain}"/htdocs;
else
  echo "repo, branch and subdomain were not provided, I will not do anything.";
fi

核心原因

PHP通过shell_exec执行命令时,运行环境和手动登录PuTTY的环境不一致:

  • 可能找不到正确的node/npm路径,导致命令异常启动后台进程而非阻塞执行;
  • 环境变量缺失(比如PATH),使得npm命令执行行为和手动运行时不同。

修复方案

1. 指定node/npm绝对路径

手动登录服务器,执行以下命令获取绝对路径:

which node
which npm

将bash脚本中的npm替换为上述命令得到的绝对路径(例如/usr/local/bin/npm),确保PHP调用时能找到正确的执行文件:

# 替换原npm命令
/usr/local/bin/npm --prefix /path/"${repo}"/ install
/usr/local/bin/npm --prefix /path/"${repo}"/ run build

2. 强制链式阻塞执行并添加错误检查

用&&连接所有命令,确保前一个命令执行成功(返回码为0)才会执行下一个,同时将输出重定向到日志文件方便排查:

if [[ ${repo} && ${branch} && ${subdomain} ]]; then
  git -C /path/"${repo}"/ checkout "${branch}" && \
  git -C /path/"${repo}"/ fetch && \
  git -C /path/"${repo}"/ pull origin "${branch}" && \
  /usr/local/bin/npm --prefix /path/"${repo}"/ install && \
  /usr/local/bin/npm --prefix /path/"${repo}"/ run build && \
  rsync -auv /path/"${repo}"/htdocs/* /path/"${subdomain}"/htdocs
else
  echo "repo, branch and subdomain were not provided, I will not do anything."
fi

3. 确保脚本完整执行

shell_exec虽会等待命令完成,但如果bash脚本中存在后台进程可能提前返回。可在bash脚本末尾添加wait命令,确保所有子进程执行完毕:

# 在rsync命令后或脚本末尾添加
wait

或者改用PHP的exec函数捕获执行状态,方便排查:

$command = "/bin/bash ./sync.sh --repo=" . $hook["repo"] . " --branch=" . $hook["branch"] . " --subdomain=" . $subdomain;
exec($command, $output, $returnVar);
# 可将$output和$returnVar写入日志,用于问题排查

4. 添加日志排查

在bash脚本关键步骤添加日志输出,确认每个命令的执行状态和时间:

LOG_FILE="/var/log/deploy.log"
echo "$(date '+%Y-%m-%d %H:%M:%S'): 开始执行npm install" >> $LOG_FILE
/usr/local/bin/npm --prefix /path/"${repo}"/ install >> $LOG_FILE 2>&1
echo "$(date '+%Y-%m-%d %H:%M:%S'): npm install完成,返回码: $?" >> $LOG_FILE

echo "$(date '+%Y-%m-%d %H:%M:%S'): 开始执行npm run build" >> $LOG_FILE
/usr/local/bin/npm --prefix /path/"${repo}"/ run build >> $LOG_FILE 2>&1
echo "$(date '+%Y-%m-%d %H:%M:%S'): npm run build完成,返回码: $?" >> $LOG_FILE

echo "$(date '+%Y-%m-%d %H:%M:%S'): 开始执行rsync同步" >> $LOG_FILE
rsync -auv /path/"${repo}"/htdocs/* /path/"${subdomain}"/htdocs >> $LOG_FILE 2>&1
echo "$(date '+%Y-%m-%d %H:%M:%S'): rsync同步完成" >> $LOG_FILE

通过查看日志中的时间戳和返回码,可确认npm命令是否真的执行完成,以及是否存在报错。

验证方法

修改后,通过Webhook触发PHP脚本,查看/var/log/deploy.log日志,确认npm install、build完成后才执行rsync。若仍有问题,检查npm命令的返回码,非0则说明命令执行失败,需排查环境依赖或权限问题。

内容的提问来源于stack exchange,提问作者Mike Willis

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 05:54:52