Skip to content

Instantly share code, notes, and snippets.

@ChaosJohn
Forked from jetfir3/download_workstation.sh
Created July 12, 2025 13:58
Show Gist options
  • Save ChaosJohn/5dc2e896ce5cee4fbfa8ab92514cce1a to your computer and use it in GitHub Desktop.
Save ChaosJohn/5dc2e896ce5cee4fbfa8ab92514cce1a to your computer and use it in GitHub Desktop.

Revisions

  1. @jetfir3 jetfir3 created this gist Mar 8, 2025.
    425 changes: 425 additions & 0 deletions download_workstation.sh
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,425 @@
    #!/usr/bin/env bash

    # Download VMware Workstation for Linux without a Broadcom account
    #
    # Use '-k' to keep the download file compressed, exiting after download.
    # Use '-v <Version>' to specify desired version (12.0.0 or higher required).

    BASE_URL="https://softwareupdate-prod.broadcom.com/cds/vmw-desktop/"
    CDN_MIRROR="softwareupdate-prod.broadcom.com:443:softwareupdate-prod.broadcom.com.cdn.cloudflare.net:443"

    read -r -d '' XML_DATA << 'EOF'
    <metalist>
    <metadata>
    <version>12.0.0</version>
    <url>ws/12.5.0/4352439/linux/core/</url>
    </metadata>
    <metadata>
    <version>14.0.0</version>
    <url>ws/14.1.0/7370693/linux/core/</url>
    </metadata>
    <metadata>
    <version>17.0.0</version>
    <url>ws/17.0.2/21581411/linux/core/</url>
    </metadata>
    <metadata>
    <version>16.0.0</version>
    <url>ws/16.2.1/18811642/linux/core/</url>
    </metadata>
    <metadata>
    <version>17.0.0</version>
    <url>ws/17.0.1/21139696/linux/core/</url>
    </metadata>
    <metadata>
    <version>14.0.0</version>
    <url>ws/14.1.6/12368378/linux/core/</url>
    </metadata>
    <metadata>
    <version>15.0.0</version>
    <url>ws/15.5.2/15785246/linux/core/</url>
    </metadata>
    <metadata>
    <version>14.0.0</version>
    <url>ws/14.0.0/6661328/linux/core/</url>
    </metadata>
    <metadata>
    <version>14.0.0</url>
    <url>ws/14.1.4/10722678/linux/core/</url>
    </metadata>
    <metadata>
    <version>14.0.0</version>
    <url>ws/14.1.7/12989993/linux/core/</url>
    </metadata>
    <metadata>
    <version>12.0.0</version>
    <url>ws/12.5.1/4542065/linux/core/</url>
    </metadata>
    <metadata>
    <version>16.0.0</version>
    <url>ws/16.2.5/20904516/linux/core/</url>
    </metadata>
    <metadata>
    <version>12.0.0</version>
    <url>ws/12.5.9/7535481/linux/core/</url>
    </metadata>
    <metadata>
    <version>15.0.0</version>
    <url>ws/15.0.0/10134415/linux/core/</url>
    </metadata>
    <metadata>
    <version>12.0.0</version>
    <url>ws/12.5.7/5813279/linux/core/</url>
    </metadata>
    <metadata>
    <version>16.0.0</version>
    <url>ws/16.1.0/17198959/linux/core/</url>
    </metadata>
    <metadata>
    <version>17.0.0</version>
    <url>ws/17.6.0/24238078/linux/core/</url>
    </metadata>
    <metadata>
    <version>17.0.0</version>
    <url>ws/17.6.1/24319023/linux/core/</url>
    </metadata>
    <metadata>
    <version>17.0.0</version>
    <url>ws/17.6.2/24409262/linux/core/</url>
    </metadata>
    <metadata>
    <version>17.0.0</version>
    <url>ws/17.6.3/24583834/linux/core/</url>
    </metadata>
    <metadata>
    <version>16.0.0</version>
    <url>ws/16.2.3/19376536/linux/core/</url>
    </metadata>
    <metadata>
    <version>16.0.0</version>
    <url>ws/16.0.0/16894299/linux/core/</url>
    </metadata>
    <metadata>
    <version>15.0.0</version>
    <url>ws/15.0.4/12990004/linux/core/</url>
    </metadata>
    <metadata>
    <version>14.0.0</version>
    <url>ws/14.1.2/8497320/linux/core/</url>
    </metadata>
    <metadata>
    <version>12.0.0</version>
    <url>ws/12.0.1/3160714/linux/core/</url>
    </metadata>
    <metadata>
    <version>12.0.0</version>
    <url>ws/12.1.0/3272444/linux/core/</url>
    </metadata>
    <metadata>
    <version>12.0.0</version>
    <url>ws/12.1.1/3770994/linux/core/</url>
    </metadata>
    <metadata>
    <version>15.0.0</version>
    <url>ws/15.5.5/16285975/linux/core/</url>
    </metadata>
    <metadata>
    <version>15.0.0</version>
    <url>ws/15.0.2/10952284/linux/core/</url>
    </metadata>
    <metadata>
    <version>17.0.0</version>
    <url>ws/17.5.0/22583795/linux/core/</url>
    </metadata>
    <metadata>
    <version>12.0.0</version>
    <url>ws/12.5.4/5192485/linux/core/</url>
    </metadata>
    <metadata>
    <version>12.0.0</version>
    <url>ws/12.5.5/5234757/linux/core/</url>
    </metadata>
    <metadata>
    <version>16.0.0</version>
    <url>ws/16.2.4/20089737/linux/core/</url>
    </metadata>
    <metadata>
    <version>15.0.0</version>
    <url>ws/15.0.1/10737736/linux/core/</url>
    </metadata>
    <metadata>
    <version>15.0.0</version>
    <url>ws/15.0.3/12422535/linux/core/</url>
    </metadata>
    <metadata>
    <version>14.0.0</version>
    <url>ws/14.1.1/7528167/linux/core/</url>
    </metadata>
    <metadata>
    <version>15.0.0</version>
    <url>ws/15.5.1/15018445/linux/core/</url>
    </metadata>
    <metadata>
    <version>14.0.0</version>
    <url>ws/14.1.5/10950780/linux/core/</url>
    </metadata>
    <metadata>
    <version>15.0.0</version>
    <url>ws/15.1.0/13591040/linux/core/</url>
    </metadata>
    <metadata>
    <version>16.0.0</version>
    <url>ws/16.1.1/17801498/linux/core/</url>
    </metadata>
    <metadata>
    <version>15.0.0</version>
    <url>ws/15.5.6/16341506/linux/core/</url>
    </metadata>
    <metadata>
    <version>15.0.0</version>
    <url>ws/15.5.0/14665864/linux/core/</url>
    </metadata>
    <metadata>
    <version>12.0.0</version>
    <url>ws/12.5.8/7098237/linux/core/</url>
    </metadata>
    <metadata>
    <version>12.0.0</version>
    <url>ws/12.5.6/5528349/linux/core/</url>
    </metadata>
    <metadata>
    <version>12.0.0</version>
    <url>ws/12.0.0/2985596/linux/core/</url>
    </metadata>
    <metadata>
    <version>15.0.0</version>
    <url>ws/15.5.7/17171714/linux/core/</url>
    </metadata>
    <metadata>
    <version>17.0.0</version>
    <url>ws/17.0.0/20800274/linux/core/</url>
    </metadata>
    <metadata>
    <version>12.0.0</version>
    <url>ws/12.5.2/4638234/linux/core/</url>
    </metadata>
    <metadata>
    <version>12.0.0</version>
    <url>ws/12.5.3/5115892/linux/core/</url>
    </metadata>
    <metadata>
    <version>17.0.0</version>
    <url>ws/17.5.2/23775571/linux/core/</url>
    </metadata>
    <metadata>
    <version>14.0.0</version>
    <url>ws/14.1.3/9474260/linux/core/</url>
    </metadata>
    <metadata>
    <version>17.0.0</version>
    <url>ws/17.5.1/23298084/linux/core/</url>
    </metadata>
    <metadata>
    <version>16.0.0</version>
    <url>ws/16.2.0/18760230/linux/core/</url>
    </metadata>
    <metadata>
    <version>16.0.0</version>
    <url>ws/16.1.2/17966106/linux/core/</url>
    </metadata>
    </metalist>
    EOF

    KEEP_COMPRESSED=false
    USER_VERSION=""
    VERSION=""
    BUILD=""

    while getopts "kv:" opt; do
    case $opt in
    k) KEEP_COMPRESSED=true ;;
    v) USER_VERSION=$OPTARG ;;
    *) echo "Usage: $0 [-k] [-v VERSION]" >&2; exit 1 ;;
    esac
    done

    requirements_check() {
    command -v curl >/dev/null 2>&1 || { echo "Error: curl is required but not installed." >&2; exit 1; }
    command -v grep >/dev/null 2>&1 || { echo "Error: grep is required but not installed." >&2; exit 1; }
    command -v awk >/dev/null 2>&1 || { echo "Error: awk is required but not installed." >&2; exit 1; }
    command -v sed >/dev/null 2>&1 || { echo "Error: sed is required but not installed." >&2; exit 1; }
    command -v tar >/dev/null 2>&1 || { echo "Error: tar is required but not installed." >&2; exit 1; }
    command -v sort >/dev/null 2>&1 || { echo "Error: sort is required but not installed." >&2; exit 1; }
    }

    requirements_check

    if [[ $(uname | tr '[:upper:]' '[:lower:]') != "linux"* ]]; then
    KEEP_COMPRESSED=true
    echo "Warning: Not running on Linux. Keeping file compressed." >&2
    fi

    if sort --version 2>/dev/null | grep -q "GNU coreutils"; then
    SORT_CMD="sort -V"
    SORT_UNIQUE_CMD="sort -uV"
    else
    SORT_CMD="sort -t. -k1,1n -k2,2n -k3,3n -k4,4n"
    SORT_UNIQUE_CMD="sort -t. -k1,1n -k2,2n -k3,3n -k4,4n -u"
    fi

    version_gte() {
    printf '%s\n%s\n' "$1" "$2" | eval "$SORT_CMD" | head -n 1 | grep -q "^$2$"
    }

    get_base_versions() {
    echo "$XML_DATA" | awk '
    BEGIN {FS="[<>]"}
    /<version>/ {
    if ($3 ~ /^[0-9]+\.0\.0$/) {
    print $3;
    }
    }' | eval "$SORT_UNIQUE_CMD"
    }

    get_full_versions_and_builds() {
    local selected_base_major="$1"
    local major_version_prefix=$(echo "$selected_base_major" | cut -d'.' -f1)

    echo "$XML_DATA" | awk -v target_major="$major_version_prefix" '
    BEGIN {
    in_metadata = 0;
    current_version_tag = "";
    }
    /<metadata>/ { in_metadata = 1; current_version_tag = ""; }
    /<\/metadata>/ { in_metadata = 0; }
    in_metadata && /<version>/ {
    current_version_tag = $0;
    gsub(/.*<version>|<\/version>.*/, "", current_version_tag);
    }
    in_metadata && /<url>/ {
    url_tag=$0;
    gsub(/.*<url>ws\/|<\/url>.*/, "", url_tag);
    split(url_tag, parts, "/");
    if (current_version_tag == target_major ".0.0" && parts[1] ~ "^" target_major "\\..*") {
    print parts[1], parts[2];
    }
    }' | eval "$SORT_UNIQUE_CMD"
    }

    get_specific_version_and_build() {
    local target_version="$1"
    local target_major_prefix=$(echo "$target_version" | cut -d'.' -f1)

    echo "$XML_DATA" | awk -v target_ver="$target_version" -v target_major_prefix_awk="$target_major_prefix" '
    BEGIN {
    FS="[<>]";
    current_version_tag_content = "";
    }
    /<metadata>/ { current_version_tag_content = ""; }
    /<version>/ { current_version_tag_content = $3; }
    /<url>/ {
    if (current_version_tag_content == target_major_prefix_awk ".0.0") {
    split($3, url_parts, "/");
    full_version = url_parts[2];
    build_number = url_parts[3];
    if (full_version == target_ver) {
    print full_version, build_number;
    exit;
    }
    }
    }'
    }

    check_url() {
    local url=$1
    curl -s --head --connect-to "$CDN_MIRROR" "$url" | head -n 1 | grep "200" >/dev/null
    }

    if [[ -n "$USER_VERSION" ]]; then
    version_gte "${USER_VERSION}" "12.0.0" || { echo "Error: Specified version ${USER_VERSION} is not supported. Workstation 12.0.0 or higher is required." >&2; exit 1; }

    read -r VERSION BUILD <<< "$(get_specific_version_and_build "$USER_VERSION")"

    [[ -z "$VERSION" || -z "$BUILD" ]] && { echo "Error: Version ${USER_VERSION} not found in the embedded XML data or URL format is unexpected." >&2; exit 1; }
    else
    echo "Available base versions:"
    base_versions_array=()
    while IFS= read -r line; do
    base_versions_array+=("$line")
    done < <(get_base_versions)

    [[ ${#base_versions_array[@]} -eq 0 ]] && { echo "No base versions found in XML data. Exiting." >&2; exit 1; }

    PS3="Select a base version: "
    select base_version_selected in "${base_versions_array[@]}"; do
    [[ -n "$base_version_selected" ]] && break || echo "Invalid selection. Please try again."
    done

    echo "Available full versions for base version $base_version_selected:"
    full_versions_builds_array=()
    while IFS= read -r line; do
    full_versions_builds_array+=("$line")
    done < <(get_full_versions_and_builds "$base_version_selected")

    [[ ${#full_versions_builds_array[@]} -eq 0 ]] && { echo "No full versions found for base version $base_version_selected. Exiting." >&2; exit 1; }

    declare -a display_full_versions
    for item in "${full_versions_builds_array[@]}"; do
    display_full_versions+=("$(echo "$item" | awk '{print $1}')")
    done

    IFS=$'\n' sorted_display_full_versions=($(printf "%s\n" "${display_full_versions[@]}" | eval "$SORT_CMD"))
    unset IFS

    PS3="Select a full version: "
    select full_version_selected in "${sorted_display_full_versions[@]}"; do
    [[ -n "$full_version_selected" ]] && break || echo "Invalid selection. Please try again."
    done

    for item in "${full_versions_builds_array[@]}"; do
    read -r ver build_num <<< "$item"
    [[ "$ver" == "$full_version_selected" ]] && { BUILD="$build_num"; break; }
    done

    [[ -z "$BUILD" ]] && { echo "Error: Could not find build number for version '$full_version_selected'. This can happen if selection is invalid." >&2; exit 1; }

    VERSION="$full_version_selected"
    fi

    echo "Selected version: $VERSION with build: $BUILD"

    DOWNLOAD_URL="${BASE_URL}ws/${VERSION}/${BUILD}/linux/core/VMware-Workstation-${VERSION}-${BUILD}.x86_64.bundle.tar"
    DOWNLOAD_DIR="${HOME}"
    DOWNLOAD_FILE="${DOWNLOAD_DIR}/VMware-Workstation-${VERSION}-${BUILD}.x86_64.bundle.tar"
    EXTRACT_DIR="${DOWNLOAD_DIR}/VMware-Workstation-${VERSION}-${BUILD}"
    BUNDLE_PATH="${EXTRACT_DIR}/VMware-Workstation-${VERSION}-${BUILD}.x86_64.bundle"

    echo "Downloading VMware Workstation v${VERSION} (${BUILD})..."
    mkdir -p "${DOWNLOAD_DIR}"

    check_url "$DOWNLOAD_URL" || { echo "Error: Installer file not found at $DOWNLOAD_URL even via CDN. Please check the version/build or XML data." >&2; exit 1; }

    curl -k -q --progress-bar -f -o "${DOWNLOAD_FILE}" --connect-to "$CDN_MIRROR" "${DOWNLOAD_URL}" || {
    echo "Error: Download failed for ${DOWNLOAD_URL}." >&2
    exit 1
    }

    ${KEEP_COMPRESSED} && { echo -e "\nFinished. Downloaded file location: ${DOWNLOAD_FILE}"; exit 0; }

    echo "Extracting tar..."
    mkdir -p "${EXTRACT_DIR}" && \
    tar -xf "${DOWNLOAD_FILE}" -C "${EXTRACT_DIR}" || {
    echo "Error: Extraction of tar file failed and may be corrupt." >&2
    echo "Downloaded file location: ${DOWNLOAD_FILE}" >&2
    rm -rf "${EXTRACT_DIR}"
    exit 1
    }

    echo "Cleaning up..."
    chmod +x "${EXTRACT_DIR}/VMware-Workstation-${VERSION}-${BUILD}.x86_64.bundle"
    rm -rf "${DOWNLOAD_FILE}" "${EXTRACT_DIR}/descriptor.xml" 2>/dev/null

    echo -e "\nFinished. You can now run the installer bundle.\nVMware Workstation bundle location: ${BUNDLE_PATH}"