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

如何在Jenkins流水线中运行Git子模块顶层的Shell脚本?

问题场景

我有一个Git子模块,顶层目录下存在delete-branches.sh脚本,在本地终端执行以下命令可正常运行:

$ git submodule foreach 'cp ../delete-branches.sh . && ./delete-branches.sh true && rm -f ./delete-branches.sh'

但将其放到Jenkins流水线(DRY_RUN为作业参数)中,写法如下时出现报错:

stage('Delete branches') {
    sshagent(credentials: ['bitbucket-creds']) {
        sh(label: "Delete branches",
           script: "git submodule foreach \'cp ../delete-branches.sh . && ./delete-branches.sh ${env.DRY_RUN}\'",
           returnStatus: true)
    }
}

报错信息:

git submodule foreach cp ../delete-branches.sh . && ./delete-branches.sh true
Entering 'module1'
./delete-branches.sh: 6: [[: not found
./delete-branches.sh: 6: [[: not found
fatal: option '--since' must come before non-option arguments
./delete-branches.sh: 6: [[: not found
./delete-branches.sh: 6: [[: not found
./delete-branches.sh: 6: [[: not found
fatal: option '--since' must come before non-option arguments

脚本内容(已添加可执行权限):

#!/bin/sh

DRY_RUN=$1
ECHO='echo '
for branch in $(git branch -a | sed 's/^\s*//' | sed 's/^remotes\///' | grep -v 'master$' | grep -v 'v2$'); do
  if ! ( [[ -f "$branch" ]] || [[ -d "$branch" ]] ) && [[ "$(git log $branch --since "1 year ago" | wc -l)" -eq 0 ]]; then
    if [[ "$DRY_RUN" = "false" ]]; then
      ECHO=""
    fi
    local_branch_name=$(echo "$branch" | sed 's/remotes\/origin\///')
    $ECHO git branch -d "${local_branch_name}"
    $ECHO git push origin --delete "${local_branch_name}"
  fi
done

求Jenkins流水线中运行该脚本的正确语法。

错误原因分析
  1. Shell语法兼容性问题:脚本开头声明的是#!/bin/sh,但使用了[[这种Bash专属语法。Jenkins的sh步骤默认调用系统sh,多数Linux发行版中sh指向Dash(而非Bash),Dash不支持[[语法,因此报错[[: not found。
  2. Git命令参数顺序错误:git log $branch --since "1 year ago"中,--since属于选项参数,必须放在分支名之前,否则Git会将其识别为分支的一部分,触发option '--since' must come before non-option arguments错误。
  3. Jenkins脚本引号处理问题:原流水线中用\'转义单引号,导致git submodule foreach的命令被拆分,无法作为整体执行;同时遗漏了本地终端命令中删除临时脚本的步骤。
解决方案

步骤1:修正脚本本身

  • 将脚本开头的#!/bin/sh改为#!/bin/bash,强制使用Bash执行以支持[[语法;
  • 调整git log的参数顺序,将--since放在分支名之前:
#!/bin/bash

DRY_RUN=$1
ECHO='echo '
for branch in $(git branch -a | sed 's/^\s*//' | sed 's/^remotes\///' | grep -v 'master$' | grep -v 'v2$'); do
  if ! ( [[ -f "$branch" ]] || [[ -d "$branch" ]] ) && [[ "$(git log --since "1 year ago" $branch | wc -l)" -eq 0 ]]; then
    if [[ "$DRY_RUN" = "false" ]]; then
      ECHO=""
    fi
    local_branch_name=$(echo "$branch" | sed 's/remotes\/origin\///')
    $ECHO git branch -d "${local_branch_name}"
    $ECHO git push origin --delete "${local_branch_name}"
  fi
done

步骤2:修正Jenkins流水线脚本

使用三重引号包裹脚本内容,确保git submodule foreach的命令作为整体传递,同时保留删除临时脚本的步骤:

stage('Delete branches') {
    sshagent(credentials: ['bitbucket-creds']) {
        sh(label: "Delete branches",
           script: """
               git submodule foreach 'cp ../delete-branches.sh . && ./delete-branches.sh ${env.DRY_RUN} && rm -f ./delete-branches.sh'
           """,
           returnStatus: true)
    }
}

若担心变量插值的转义问题,也可通过Shell环境变量传递DRY_RUN参数:

stage('Delete branches') {
    sshagent(credentials: ['bitbucket-creds']) {
        sh(label: "Delete branches",
           script: 'git submodule foreach \'cp ../delete-branches.sh . && ./delete-branches.sh $DRY_RUN && rm -f ./delete-branches.sh\'',
           returnStatus: true,
           environment: ['DRY_RUN': env.DRY_RUN])
    }
}

内容的提问来源于stack exchange,提问作者Chris F

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 03:05:16