Search code examples
bashshportabilitydebian-based

Bash - Text stored in variable is not recognized when put it as argument


I am trying to make a script that automates download of SpigotMC BuildTools, detect input version ( sh build.sh <version> ) , and set a variable which is used if the version is greater or equal with 1.14 (since this release, SpigotMC discontinued the automation of auto-compilation of craftbukkit by default. Now it compiles only the spigot jar and I need both of them compiled for my personal purposes.)

Here is what I have tried (I'm a Linux newbie, so this might be messed up):

#!/bin/bash

# Sources:
# Append variables in bash                : https://www.cyberciti.biz/faq/howto-linux-unix-bash-append-textto-variables/
# Check MANIFEST.MF content from jar file : https://www.manongdao.com/q-106728.html
# Check for specified property json file  : https://stackoverflow.com/questions/34543829/jq-cannot-index-array-with-string
#                                           https://www.ultralinux.org/post/json-bash/
# Bash Array                              : https://unix.stackexchange.com/questions/253892/syntax-error-unexpected-when-creating-an-array
# #######################################################################################################################################
# 
# #######################################################################################################################################
# BuildTools Jar Info Variables
# -----------------------------
# BuilsTools working directory
buildtools_root="$(pwd)" # We use $(command) to store "command" output in a variable. We use ${command} if we have to stick other strings near it without whitespace.
# BuildTools jar location
buildtools_jar="$buildtools_root/BuildTools.jar"
# Current BuildTools jar version
buildtools_ver="$(unzip -p "$buildtools_jar" META-INF/MANIFEST.MF | grep 'Implementation-Version:' | cut -d '-' -f 5)"
# Jenkins Api-related variables. 
# A. Retrieve BuildTools lastSuccessfulBuild link
targetJar="$(curl -s 'https://hub.spigotmc.org/jenkins/job/BuildTools/lastSuccessfulBuild/api/json' | jq -r '.artifacts[].relativePath')"
lastSuccessfulBuild="$(curl -s 'https://hub.spigotmc.org/jenkins/job/BuildTools/lastSuccessfulBuild/api/json' | jq -r '.url')"
# B. Make the download link
latestBuildToolsUrl="${lastSuccessfulBuild}artifacts/$targetJar"
# Latest BuildTools Build Version
latestBuildToolsVersion="$(curl -s 'https://hub.spigotmc.org/jenkins/job/BuildTools/lastSuccessfulBuild/api/json' | jq -r '.number')"
displayFullName="$(curl -s 'https://hub.spigotmc.org/jenkins/job/BuildTools/lastSuccessfulBuild/api/json' | jq -r '.fullDisplayName')"
# ---------------------------------------------------------------------------------------------------------------------------------------
# BuildTools Jar Run Variables
# -----------------------------
# 
# #######################################################################################################################################

# This is a function to set java home to current java version in use.
javahome_set () {
    JAVA_HOME=$(dirname "$(dirname "$( readlink -f /etc/alternatives/java )")")

    OIFS=$IFS
    IFS=':';
    for i in $VAR;
    do
            JAVA1=$i/bin/java
            JAVA2=$i/java
            if [ -d "$i" ];
            then
                    if [ ! -L "$JAVA1" ] && [ -x "$JAVA1" ] || [ ! -L "$JAVA2" ] && [ -x "$JAVA2" ]; then
                        echo "dropping path: $i";
                    else
                        NEW=$NEW:$i
                    fi
            fi
    done
    IFS=$OIFS
    JAVA_HOME=$NEW:$JAVA_HOME/bin
    JAVA_HOME=${JAVA_HOME#:*}

}
javahome_set

# This function requires arguments. Checks if $1 >= $2
vercomp () {
    if [[ $1 == $2 ]]
    then
        return 0
    fi
    local IFS=.
    local i ver1=($1) ver2=($2)
    # fill empty fields in ver1 with zeros
    for ((i=${#ver1[@]}; i<${#ver2[@]}; i++))
    do
        ver1[i]=0
    done
    for ((i=0; i<${#ver1[@]}; i++))
    do
        if [[ -z ${ver2[i]} ]]
        then
            # fill empty fields in ver2 with zeros
            ver2[i]=0
        fi
        if ((10#${ver1[i]} > 10#${ver2[i]}))
        then
            return 1
        fi
        if ((10#${ver1[i]} < 10#${ver2[i]}))
        then
            return 2
        fi
    done
    return 0
}

# This is a function to download the latest BuildTools version and check if download is successful.
buildtools_download () {
    curl --silent "$latestBuildToolsUrl" --output BuildTools.jar #Request download to BuildTools.
    if [ "$?" -eq 0 ]; then #Check if file was downloaded. It returns error code non-zero if file was not properly downloaded. (in this  language)
        echo "$Green Successful downloaded BuildTools.$Color_Off"
    else
        echo "$Red Error while downloading BuildTools. $Color_Off"
    fi
}

# This is a function to check for updates for BuildTools jar
buildtools_check () {
    if [ ! -e "$buildtools_jar" ]; then #If BuildTools.jar does NOT exist in "BuildTools" folder
        echo "$Yellow Downloading BuildTools..."
        buildtools_download 
        exit
    elif [ -e "$buildtools_jar" ]; then
        if [ "$buildtools_ver" -lt "$latestBuildToolsVersion" ]; then
            echo "$Blue Updating BuildTools... $Color_Off"
            rm "$buildtools_jar"
            buildtools_download
        fi
    else
        echo "$Cyan BuildTools is up to date. $Color_Off"
    fi
}

info_menu () {
echo "==============================================[Local-Info]=============================================="
echo "BuildTools Root     : $buildtools_root"
echo "Executable Path     : $buildtools_jar"
echo "Installed Version   : $buildtools_ver"
echo "JAVA_HOME Directory : $JAVA_HOME"
echo "=============================================[JsonAPI-Info]============================================="
echo "Latest Download Url: $latestBuildToolsUrl"
echo "========================================================================================================"
echo
if [ "$buildtools_ver" -eq "$latestBuildToolsVersion" ]; then
    echo "$Green You have the latest SpigotMC BuildTools version. $Color_Off"
else
    echo "$Yellow A new build is available : $displayFullName"
fi
echo
}

if [ -z "$1" ]; then
    echo "$BCyan Usage: $0 $BBlue<version> $Color_Off" & exit #By default, $0 is the name of this file
else
    if [ -d "$1" ]; then #If argument is defined
        if [ "$1" = "latest" ]; then
            buildtools_check
        else
            vercomp "$1" "1.14"
            if [ $? -eq 0 -o $? -eq 1 ]; then
                BothJars=(--compile craftbukkit,spigot)  # means $1 >= 1.14 ; sets the $BothJars variable
                buildtools_check
            fi
        if [ "$1" -lt "1.14" ]; then
            echo "$Yellow You want to build an older server version. Good choice btw."
            buildtools_check
        fi
    fi
fi
    rm -rf "$1" && mkdir "$1"
    cd "$1" || exit
    java -jar ./BuildTools.jar --rev "$1" "${BothJars[@]}" --generate-source --generate-docs #../filename is for executing it from one dir far.


I have put comments for you to understand what's going on.

Can someone help me with the problem?

EDIT : If you ever played Minecraft, or if you saw different versions of Minecraft, you can encounter the following version formats:

1.7.10 ; 1.8 ; 1.13.2 ; 1.14 ; 1.15.3 (and no, there is not a '1.8.0' version. Just '1.8')

Here are examples of comparisons between versions which are mathematical false after tr command :

1.8 > 1.7.10 - 18 > 1710
1.14 > 1.13.2 - 114 > 1132

1.15.2 < 1.16 - 1152 < 116

So... There may be problems that could affect the comparison of those numbers, since they are versions, not fractions or integers.

EDIT 2 : I will not make more than 3 edits / post. Thank you Richard K. for helping me with version checking function! I have rewritten my script to make it more complete. I store important parts in functions to save space and to be more visible. Summary : I don't understand what have I done wrong here. U_U

EDIT 3 : As of Rachid K's answer, I got this part to test:

#!/bin/bash

vercomp () {
    if [[ $1 == $2 ]]
    then
        return 0
    fi
    local IFS=.
    local i ver1=($1) ver2=($2)
    # fill empty fields in ver1 with zeros
    for ((i=${#ver1[@]}; i<${#ver2[@]}; i++))
    do
        ver1[i]=0
    done
    for ((i=0; i<${#ver1[@]}; i++))
    do
        if [[ -z ${ver2[i]} ]]
        then
            # fill empty fields in ver2 with zeros
            ver2[i]=0
        fi
        if ((10#${ver1[i]} > 10#${ver2[i]}))
        then
            return 1
        fi
        if ((10#${ver1[i]} < 10#${ver2[i]}))
        then
            return 2
        fi
    done
    return 0
}

if [ -z "$1" ]; then
    echo "$BCyan Usage: $0 $BBlue<version> $Color_Off" & exit #By default, $0 is the name of this file
else
    if [ -d "$1" ]; then #If argument is defined
        if [ "$1" = "latest" ]; then
            echo "Building latest version"
        else
            vercomp "$1" "1.14"
            rc=$?
            case $rc in
            0)
            echo " You want to build version 1.14"
            ;;
            1)
            echo " You want to build version above 1.14"
            ;;
            2)
            echo " You want to build version below 1.14"
            ;;
            latest)
            echo " You want to build latest version."
            ;;
            esac
        fi
    fi
fi

So the vercomp() should see if : $1 is equal with string latest ; $1 is greater or equal with 1.14 ; $1 is lower than 1.14.

The function looks good, but the echo command does not display. I noticed that in some cases the echo command doesn't display due to missing double quotes. So... putting that at it. But it doesn't work. Why?


Solution

  • Apparently the version you get is either a number like 1.14 or the "latest" string. You need to split you check in two tests. First you check the "latest" value :

    if [ "$1" = "latest" ]
    

    and if it is false, check the versions by using the solution proposed in this post

    vercomp () {
        if [[ $1 == $2 ]]
        then
            return 0
        fi
        local IFS=.
        local i ver1=($1) ver2=($2)
        # fill empty fields in ver1 with zeros
        for ((i=${#ver1[@]}; i<${#ver2[@]}; i++))
        do
            ver1[i]=0
        done
        for ((i=0; i<${#ver1[@]}; i++))
        do
            if [[ -z ${ver2[i]} ]]
            then
                # fill empty fields in ver2 with zeros
                ver2[i]=0
            fi
            if ((10#${ver1[i]} > 10#${ver2[i]}))
            then
                return 1
            fi
            if ((10#${ver1[i]} < 10#${ver2[i]}))
            then
                return 2
            fi
        done
        return 0
    }
    

    So, to compare the version just do:

    vercomp "$1" "1.14"
    rc=$?
    case $rc in
        0) echo '=';;
        1) echo '>';;
        2) echo '<';;
    esac