jenkinsjenkins-pipelinejenkins-groovyjenkins-job-dsl

variable substitution in Jenkins pipeline fails for expression with multiple variables


While using assignment of Jenkins env variables in shell block I've noticed that it doesn't work if I'm substituting multiple variables in a string, or if there's an _. For example, this works

script {
    def formattedDate = sh(script: "date +'%Y-%m-%d %H:%M:%S'", returnStdout: true).trim()
    sh """
        mkdir customization
        mkdir customization/nemo
        mkdir customization/memo
        mkdir customization/pemo
        
        echo "BuildNum=${currentBuild.number}" > DevOpsReleaseInfo.properties
        echo "ReleaseNum=${VERSION}" >> DevOpsReleaseInfo.properties
        echo "DevOpsReleaseNum=${VERSION}" >> DevOpsReleaseInfo.properties
        echo "BuildTime=$formattedDate" >> DevOpsReleaseInfo.properties

        version="${env.VERSION}"
        build_num="${currentBuild.number}"
        for dir in customization/*; do   
            [ -f \$dir ] && continue
            customer=\$(basename \$dir)
            mkdir customization/\$customer/\$version-db-\$build_num
        done
    """
}

I get the output

+ version=6.12.0-SNAPSHOT
+ build_num=28
+ '[' -f customization/memo ]
+ basename customization/memo
+ customer=memo
+ mkdir customization/memo/6.12.0-SNAPSHOT-db-28
+ '[' -f customization/nemo ]
+ basename customization/nemo
+ customer=nemo
+ mkdir customization/nemo/6.12.0-SNAPSHOT-db-28
+ '[' -f customization/pemo ]
+ basename customization/pemo
+ customer=pemo
+ mkdir customization/pemo/6.12.0-SNAPSHOT-db-28 

but this doesn't

script {
    def formattedDate = sh(script: "date +'%Y-%m-%d %H:%M:%S'", returnStdout: true).trim()
    sh """
        mkdir customization
        mkdir customization/nemo
        mkdir customization/memo
        mkdir customization/pemo
        
        echo "BuildNum=${currentBuild.number}" > DevOpsReleaseInfo.properties
        echo "ReleaseNum=${VERSION}" >> DevOpsReleaseInfo.properties
        echo "DevOpsReleaseNum=${VERSION}" >> DevOpsReleaseInfo.properties
        echo "BuildTime=$formattedDate" >> DevOpsReleaseInfo.properties

        version="${env.VERSION}"
        build_num="${currentBuild.number}"
        for dir in customization/*; do   
            [ -f \$dir ] && continue
            customer=\$(basename \$dir)
            mkdir customization/\$customer/\$version_db_\$build_num
        done
    """
}

notice that I'm using \$version_db_\$build_num instead of \$version-db-\$build_num

+ version=6.12.0-SNAPSHOT
+ build_num=29
+ '[' -f customization/memo ]
+ basename customization/memo
+ customer=memo
+ mkdir customization/memo/29
+ '[' -f customization/nemo ]
+ basename customization/nemo
+ customer=nemo
+ mkdir customization/nemo/29
+ '[' -f customization/pemo ]
+ basename customization/pemo
+ customer=pemo
+ mkdir customization/pemo/29

Solution

  • This has to do with bash variable naming conventions.

    When combining variables and strings, the correct approach is to use curly braces ( {} ) to separate the variables from the strings.

    Underscores ( _ ) are allowed in variable names in bash, whereas Hypens ( - ) are not.

    So, $version_db_$build_num just contains 2 variables - $version_db_ and $build_num

    The value for '$version_db_' is empty as it is never defined. This is shown in output '29'.

    In case of $version-db-$build_num, it contains 2 variables and a string - $version, -db-, $build_num. So it is shown as '6.12.0-SNAPSHOT-db-28'.

    Using when using curly braces, ${version}_db_${build_num} should work.