#!/bin/sh # # By Gengik84, rewritten by PMheart # # Based on Pike R. Alpha's script and crazybirdy's modifications. # # Many thanks to PikeRAlpha, crazybirdy and PMheart! # # uncomment to enable debug logging # set -x # Change additional shell optional behavior (expand unmatched names to a null string). shopt -s nullglob # # VARIABLES # #---------------------------------------------------------------------------------------------------- # # script version # gScriptVersion="2.0.0" # # Beta version (ONLY the latest Beta) # could be empty when there is no Beta available. (consider 10.14.6) # gBetaVersion="10.15" # # stable version (ONLY the latest stable) # gStableVersion="10.14.5" # # Current macOS version returned from sw_vers (e.g 10.14.5). # gProductVersion="$(sw_vers -productVersion)" # # Current macOS build returned from sw_vers (e.g 18F132). # gProductBuildVersion="$(sw_vers -buildVersion)" # # Current macOS major version. # For example: given gProductVersion 10.14.3, gProductMajorVersion will be 14. # gProductMajorVersion="${gProductVersion:3:2}" # # Current macOS minor version. # For example: given gProductVersion 10.14.3, gProductMinorVersion will be 3. # gProductMinorVersion="${gProductVersion:6:2}" # NOTE: It will be null when no minor version available. Appending 0. [[ -z "${gProductMinorVersion}" ]] && gProductMinorVersion=0 # # Beta Major version # For example: given gBetaVersion 10.14.4, gBetaMajorVersion will be 14. # gBetaMajorVersion="${gBetaVersion:3:2}" # # Beta Minor version # For example: given gBetaVersion 10.14.4, gBetaMinorVersion will be 4. # gBetaMinorVersion="${gBetaVersion:6:2}" # NOTE: It will be null when no minor version available. Appending 0. [[ -z "${gBetaMinorVersion}" ]] && gBetaMinorVersion=0 # # Stable Major version # For example: given gStableVersion 10.14.4, gStableMajorVersion will be 14. # gStableMajorVersion="${gStableVersion:3:2}" # # Stable Minor version # For example: given gStableVersion 10.14.3, gStableMinorVersion will be 3. # gStableMinorVersion="${gStableVersion:6:2}" # NOTE: It will be null when no minor version available. Appending 0. [[ -z "${gStableMinorVersion}" ]] && gStableMinorVersion=0 # # Package Mode: # Delta - a more lightweight package, should only be used on the most recent macOS. # Combo - a reasonably full package, could be used on all major releases, used by default. # # When to use Delta (pseudo code): # gProductMajorVersion == gBetaMajorVersion && # gProductMinorVersion == gBetaMinorVersion [e.g update from 10.14.4 b1 to 10.14.4 b3] || # gProductMinorVersion == (gBetaMinorVersion - 1) [e.g update from 10.14.3 to 10.14.4 b3] #=============================================================================================================== # gProductMajorVersion == gStableMajorVersion && # gProductMinorVersion == gStableMinorVersion [e.g update from 10.14.3 to 10.14.3 supplemental update] || # gProductMinorVersion == (gStableMinorVersion - 1) [e.g update from 10.14.2 to 10.14.3] # # When to use Combo: # Of course, scenarios other than ones using Delta... # gPackageMode="Delta" #---------------------------------------------------------------------------------------------------- # # GLOBAL VARS # WILL BE UPDATED BY CERTAIN FUNCTIONS # #---------------------------------------------------------------------------------------------------- # selected channel for determining URL # will be updated by selectChannel() gChannel="" # selected catalog URL (see channels and catalogs above) # will be updated by selectChannel() gCatalog="" # selected system version gVersion="" # target destination gVolume="" # package keys # will be updated by parseCatalog() gUrlIndex="" gKeyNum="" gKeyIndex="" gKeySalt="" # the exact url gUrl="" # distribution file gDist="" # initialisation of a variable (our target folder). gTmpDirectory="/tmp" gWorkDir="" # name of target installer package gInstallerPackage="installer.pkg" # backup dir gUserPath="${HOME}/Desktop" #---------------------------------------------------------------------------------------------------- # # CONSTANTS # NO CHANGE NEEDED HERE # #---------------------------------------------------------------------------------------------------- # possible channels gChannels=( # dev beta (pub beta has been dropped) "Beta" # normal "Normal Channel" ) # possible catalog URLs (corresponding to channels above) gCatalogs=( # beta "https://swscan.apple.com/content/catalogs/others/index-10.15seed-10.15-10.14-10.13-10.12-10.11-10.10-10.9-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog.gz" # normal "https://swscan.apple.com/content/catalogs/others/index-10.14-10.13-10.12-10.11-10.10-10.9-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog.gz" ) # colors RED="\e[31m" GREEN="\e[32m" DARK_YELLOW="\e[33m" BLUE="\e[34m" PURPLE="\e[35m" CYAN="\e[36m" OFF="\e[0m" #---------------------------------------------------------------------------------------------------- function STY_LINE() { printf '%s\n\n' '-------------------------------------------------------------------------------' } function isProductBeta() { # if the last char of build version is a lowercased letter, # then it is Beta, 1 will be returned. local chBetaIndicator="${gProductBuildVersion:-1}" if [[ "${chBetaIndicator}" =~ [a-z] ]]; then echo 1 return fi echo 0 } function setPackageMode() { if [[ "${gChannel}" == 'Beta' ]]; then let "gBetaMinorVersionMinusOne = gBetaMinorVersion - 1" if [[ "${gProductMajorVersion}" -eq "${gBetaMajorVersion}" && \ "${gProductMinorVersion}" -eq "${gBetaMinorVersion}" || \ "${gProductMinorVersion}" -eq "${gBetaMinorVersionMinusOne}" ]]; then printf "\nUsing ${GREEN}Delta${OFF} package (Beta).\n\n" gPackageMode='Delta' else printf "\nUsing ${DARK_YELLOW}Combo${OFF} package (Beta).\n\n" fi else let "gStableMinorVersionMinusOne = gStableMinorVersion - 1" if [[ "${gProductMajorVersion}" -eq "${gStableMajorVersion}" && \ "${gProductMinorVersion}" -eq "${gStableMinorVersion}" ]]; then printf "\nUsing ${GREEN}Delta Supplemental${OFF} package (Stable).\n\n" gPackageMode='DeltaSup' elif [[ "${gProductMajorVersion}" -eq "${gStableMajorVersion}" && \ "${gProductMinorVersion}" -eq "${gStableMinorVersionMinusOne}" ]]; then printf "\nUsing ${GREEN}Delta${OFF} package (Stable).\n\n" gPackageMode='Delta' else printf "\nUsing ${DARK_YELLOW}Combo${OFF} package (Stable).\n\n" fi fi } function updateScript() { local remoteAddr="https://raw.githubusercontent.com/Gengik84/MacOS_Updater/Catalina/MacOS_Updater" local tmpScriptPath="/tmp/MacOS_Updater" # download remote script rm -f "${tmpScriptPath}" && curl -s "${remoteAddr}" -o "${tmpScriptPath}" || exit 1 local remoteVer=$(cat ${tmpScriptPath} | grep '^gScriptVersion=' | tr -cd '.0-9') if [[ "${gScriptVersion}" != "${remoteVer}" ]]; then clear printf "\tLocal Script Version: ${RED}${gScriptVersion}${OFF}\t\tUpdate available: ${GREEN}v${remoteVer}${OFF}\n" local confirmScptUpd read -p "Do you want update to the latest version? (y/n) " confirmScptUpd case "${confirmScptUpd}" in y|Y ) cp "${tmpScriptPath}" "$0" STY_LINE printf "${BLUE}Running the new script...${OFF}" local args="$@" "$0" "${args}" # do not run the current (old) script again exit 0 ;; esac fi } function downloadFile() { local link="$1" local out="$2" local attempt=0 local maxTry=3 for ((; attempt < maxTry; attempt++ )); do curl "${link}" -o "${out}" if [[ $? -ne 0 ]]; then # failed, remove the "corrupted" file and try again rm -rf "${out}" else # success return 0 fi sleep 0.5 done printf "Failed to download ${RED}${out}${OFF} after ${attempt} times trying!\n" exit 1 } function registerScript() { local prefDir="${HOME}/.MacOS_Updater" local prefFile="${prefDir}/pref.conf" [[ ! -d "${prefDir}" ]] && mkdir "${prefDir}" if [[ -f "${prefFile}" ]]; then source "${prefFile}" else clear echo 'It seems to be the first time you are using this script,' echo 'for convenience, you may want to register it,' echo 'in this case you will no more be prompted to enter the password' echo 'until having been moved to elsewhere.' echo 'Note that it might also be a security risk when registering.' echo echo 'Well, it is totally up to you to register or not,' echo 'and you will be no more asked you after this time.' echo while true; do local confirmRegScpt read -p "Would you like to? (y/n) " confirmRegScpt case "${confirmRegScpt}" in y|Y ) echo 'wantRegScpt=1' > "${prefFile}" break ;; n|N ) echo 'wantRegScpt=0' > "${prefFile}" break ;; * ) echo 'Try again...' ;; esac done fi if [[ "${wantRegScpt}" -eq 1 ]]; then local regFile="/etc/sudoers.d/MacOS_Updater" local context="$(users) ALL=(ALL) NOPASSWD: ${0}" if [[ ! -f "${regFile}" || \ "$(cat ${regFile})" != "${context}" ]]; then rm -f "${regFile}" echo "${context}" | sudo tee "${regFile}" &> /dev/null fi fi } function selectChannel() { local i=0 while true; do # list all possible channels printf "${CYAN}SELECT A CHANNEL${OFF}\n" for c in "${gChannels[@]}"; do printf "[ ${GREEN}${i}${OFF} ] ${PURPLE}${c}${OFF}\n" let i++ done local channelNum read -p ">> " channelNum # insane choice?! if [[ -z "${channelNum}" || ! "${channelNum}" =~ ^[0-9]+$ || "${channelNum}" -lt 0 || "${channelNum}" -ge $i ]]; then printf "${RED}Wrong choice!${OFF}\n\n" # restore i to 0 for another traversal i=0 continue fi # update vars gChannel="${gChannels[channelNum]}" gCatalog="${gCatalogs[channelNum]}" # ask for confirmation printf "Your choice: [ ${GREEN}${channelNum}${OFF} ] ${PURPLE}${gChannel}${OFF}\n" while true; do local confirmChannel read -p ">> CONFIRM? (Y/N) " confirmChannel case "${confirmChannel}" in "" ) printf 'Please do type Y if you are sure.\n' continue ;; y|Y ) # done, terminate the function return ;; n|N ) printf 'goodbye\n' exit 0 ;; * ) printf 'Try again...\n\n' # restore i to 0 for another traversal i=0 # go to the last loop break ;; esac done done } function getURLIndex() { local str2Grep="${1}" echo "$(cat /tmp/update-catalogs | \ grep "${str2Grep}" | grep pkg | grep -v RecoveryHDUpdate | grep -v Patch | grep -v integrityData | \ sed 's///' | sed 's/<\/string>//' | \ awk '{print $1;}' | sed 's/^.*downloads//')" } function parseCatalog() { selectChannel # show version if [[ "${gChannel}" == "Beta" && ! -z "${gBetaVersion}" ]]; then gVersion="${gBetaVersion}" elif [[ "${gChannel}" == "Beta" && -z "${gBetaVersion}" ]]; then printf "${RED}Warning:${OFF} using Beta channel but there is no release at the moment!\n" printf "Redirecting to ${PURPLE}Normal Channel ${OFF}and using version ${GREEN}${gStableVersion}${OFF}.\n" # manually put normal channel and stable version here for redirection gChannel="${gChannels[1]}" gCatalog="${gCatalogs[1]}" gVersion="${gStableVersion}" sleep 2 elif [[ "${gChannel}" != "Beta" ]]; then gVersion="${gStableVersion}" fi # check if it is fine to go with Delta setPackageMode # download catalog cd "${gTmpDirectory}" [[ -f update-catalogs ]] && rm -f update-catalogs downloadFile "${gCatalog}" "update-catalogs.gz" && gunzip update-catalogs.gz || exit 1 # default value for Delta local str2Grep="macOSUpd${gVersion}.pkg" case "${gPackageMode}" in 'Combo' ) str2Grep="macOSUpdCombo${gVersion}" ;; 'DeltaSup' ) str2Grep="macOSUpd${gVersion}Supplemental" ;; esac # update vars gUrlIndex="$(getURLIndex "${str2Grep}")" # # When upgrading, for example, # from 10.14.5 Beta (18F131a) # to 10.14.5 Final (18F132), # gPackageMode would be set to DeltaSup, which is fine, # yet supplemental update may not exist # all the time, that is to say that gUrlIndex is null. # We are going to be back to Delta in this case and check again. # local isBeta="$(isProductBeta)" if [[ "${isBeta}" -eq 1 && \ "${gPackageMode}" == 'DeltaSup' && \ -z "${gUrlIndex}" ]]; then printf 'You are on Beta and going to update to supplemental update,\nbut there is nothing at the moment!\nUpdating to the latest stable version via Delta.\n\n' gPackageMode='Delta' str2Grep="macOSUpd${gVersion}.pkg" gUrlIndex="$(getURLIndex "${str2Grep}")" fi if [[ "${gChannel}" == "Normal Channel" && \ -z "${gUrlIndex}" && \ "${gProductVersion}" == "${gStableVersion}" ]]; then printf "You are already ${GREEN}up to date${OFF}!\n" exit 0 fi [[ -z "${gUrlIndex}" ]] && printf "ERROR: ${RED}URL Index not found!${OFF}\n" && exit 1 gKeyNum="$( echo ${gUrlIndex} | cut -d/ -f2,3)" gKeyIndex="$(echo ${gUrlIndex} | cut -d/ -f4,4)" gKeySalt="$( echo ${gUrlIndex} | cut -d/ -f5,5)" gUrl="https://swdist.apple.com/content/downloads/${gKeyNum}/${gKeyIndex}/${gKeySalt}/" gDist="${gKeyIndex}.English.dist" # create our working dir gWorkDir="${gTmpDirectory}/${gKeyIndex}" [[ ! -d "${gWorkDir}" ]] && mkdir "${gWorkDir}" cd "${gWorkDir}" # download dist rm -f "${gDist}" printf "Downloading: ${BLUE}${gDist}${OFF}...\n" downloadFile "${gUrl}${gDist}" "${gDist}" # show build local awkNR=0 [[ "${gChannel}" == "Beta" && ! -z "${gBetaVersion}" ]] && awkNR=15 || awkNR=12 build="$(cat ${gDist} | \ sed 's///' | sed 's/<\/string>//' | \ awk '{print $1;}' | awk NR==${awkNR})" confirmDownload } function confirmDownload() { while true; do printf "${RED}${gVersion} (${build})${OFF} will be downloaded, confirm? (y/n) " local confirmVer read -ea confirmVer case "${confirmVer}" in "" ) printf 'Please do type Y if you are sure.\n' continue ;; y|Y ) # done, terminate the function return ;; n|N ) rm -rf "${gWorkDir}" printf 'goodbye\n' exit 0 ;; * ) printf 'Try again...\n\n' continue ;; esac done } function downloadPackage() { # now we are inside "${gWorkDir}" thanks to the prior parseCatalog() # packages to be downloaded if [[ "${gPackageMode}" == 'Combo' ]]; then # Looks like only 10.14.4 has Auto suffixed and others do not. # TODO: find when Auto exists and when not. if [[ "${gVersion}" == '10.14.4' ]]; then local targetFiles=( "macOSUpdCombo${gVersion}Auto.pkg" "macOSUpdCombo${gVersion}Auto.RecoveryHDUpdate.pkg" "macOSBrain.pkg" "SecureBoot.pkg" "EmbeddedOSFirmware.pkg" "FirmwareUpdate.pkg" "FullBundleUpdate.pkg" ) else local targetFiles=( "macOSUpdCombo${gVersion}.pkg" "macOSUpdCombo${gVersion}.RecoveryHDUpdate.pkg" "macOSBrain.pkg" "SecureBoot.pkg" "EmbeddedOSFirmware.pkg" "FirmwareUpdate.pkg" "FullBundleUpdate.pkg" ) fi elif [[ "${gPackageMode}" == 'DeltaSup' ]]; then local targetFiles=( "macOSUpd${gVersion}Supplemental.pkg" "macOSUpd${gVersion}Supplemental.RecoveryHDUpdate.pkg" "macOSBrain.pkg" "SecureBoot.pkg" "EmbeddedOSFirmware.pkg" "FirmwareUpdate.pkg" "FullBundleUpdate.pkg" ) else local targetFiles=( "macOSUpd${gVersion}.pkg" "macOSUpd${gVersion}.RecoveryHDUpdate.pkg" "macOSBrain.pkg" "SecureBoot.pkg" "EmbeddedOSFirmware.pkg" "FirmwareUpdate.pkg" "FullBundleUpdate.pkg" ) fi # download them clear for filename in "${targetFiles[@]}"; do if [[ ! -e "${filename}" ]]; then printf "Downloading: ${BLUE}${filename}${OFF}...\n" downloadFile "${gUrl}${filename}" "${filename}" elif [[ -z "$(pkgutil --check-signature ${filename} | grep -i 'signed Apple Software')" ]]; then printf "File: ${filename} is ${RED}CORRUPTED${OFF}, redownloading...\n" rm -rf "${filename}" downloadFile "${gUrl}${filename}" "${filename}" else printf "File: ${GREEN}${filename}${OFF} is already there, ${DARK_YELLOW}skipping download.${OFF}\n" fi STY_LINE done } function selectDestination() { cd /Volumes local i=0 local targetVolumes=(*) while true; do printf "${CYAN}SELECT A DESTINATION${OFF}\n" for vol in "${targetVolumes[@]}"; do printf "[ ${GREEN}${i}${OFF} ] ${CYAN}${vol}${OFF}\n" let i++ done # ask to select a target volume local volNum read -p ">> " volNum # insane choice?! if [[ -z "${volNum}" || ! "${volNum}" =~ ^[0-9]+$ || "${volNum}" -lt 0 || "${volNum}" -ge $i ]]; then printf "${RED}Wrong choice!${OFF}\n\n" # restore i to 0 for another traversal i=0 continue fi local volume="${targetVolumes[volNum]}" gVolume="/Volumes/${volume}" printf "Your choice: [ ${GREEN}${volNum}${OFF} ] ${CYAN}${volume}${OFF}\n" while true; do local confirmVol read -p ">> CONFIRM? (y/n) " confirmVol case "${confirmVol}" in "" ) printf 'Please do type Y if you are sure.\n' continue ;; y|Y ) # done, terminate the function return ;; * ) printf 'Try again...\n\n' # restore i to 0 for another traversal i=0 # go to the last loop break ;; esac done done # create the enrollment plist local seedEnrollmentPlist="${vol}/Users/Shared/.SeedEnrollment.plist" # write enrollement plist when missing (seed program options: CustomerSeed, DeveloperSeed) if [[ ! -f "${seedEnrollmentPlist}" ]]; then echo '' > "${seedEnrollmentPlist}" echo '' >> "${seedEnrollmentPlist}" echo '' >> "${seedEnrollmentPlist}" echo ' ' >> "${seedEnrollmentPlist}" echo ' SeedProgram' >> "${seedEnrollmentPlist}" if [[ "${gChannel}" == "Beta" ]]; then echo ' DeveloperSeed' >> "${seedEnrollmentPlist}" else echo ' CustomerSeed' >> "${seedEnrollmentPlist}" fi echo ' ' >> "${seedEnrollmentPlist}" echo '' >> "${seedEnrollmentPlist}" fi } function updateRecovery() { printf "${CYAN}UPDATING RECOVERY${OFF}\n" cd "${gTmpDirectory}" rm -rf "updRec" && mkdir "updRec" && cd "updRec" # download dm for updating Recovery curl -O "https://raw.githubusercontent.com/Gengik84/MacOS_Updater/master/dm" && chmod +x dm || exit 1 local dmBin="${gTmpDirectory}/updRec/dm" if [[ "${gPackageMode}" == 'Combo' ]]; then # Looks like only 10.14.4 has Auto suffixed and others do not. # TODO: find when Auto exists and when not. if [[ "${gVersion}" == '10.14.4' ]]; then local recPkg="${gWorkDir}/macOSUpdCombo${gVersion}Auto.RecoveryHDUpdate.pkg" else local recPkg="${gWorkDir}/macOSUpdCombo${gVersion}.RecoveryHDUpdate.pkg" fi elif [[ "${gPackageMode}" == 'DeltaSup' ]]; then local recPkg="${gWorkDir}/macOSUpd${gVersion}Supplemental.RecoveryHDUpdate.pkg" else local recPkg="${gWorkDir}/macOSUpd${gVersion}.RecoveryHDUpdate.pkg" fi [[ ! -f "${recPkg}" ]] && printf "ERROR: ${RED}${recPkg} NOT found!${OFF}\n" && exit 1 local tmpRecUpdDir="/tmp/RecoveryHDUpdate" rm -rf "${tmpRecUpdDir}" && pkgutil --expand-full "${recPkg}" "${tmpRecUpdDir}" local tmpDmg="${tmpRecUpdDir}/RecoveryHDMeta.dmg" # attempt to create a temporary mount point local tmpMountPoint="$(/usr/bin/mktemp -d)" # mount tmpDmg there /usr/bin/hdiutil attach -nobrowse "${tmpDmg}" -mountpoint "${tmpMountPoint}" # probe target volume local fsType="$(diskutil info "${gVolume}" | awk '$1 == "Type" { print $NF }')" # start updating if [[ "${fsType}" == "apfs" ]]; then "${dmBin}" ensureRecoveryBooter "${gVolume}" -base "${tmpMountPoint}/BaseSystem.dmg" "${tmpMountPoint}/BaseSystem.chunklist" -diag "${tmpMountPoint}/AppleDiagnostics.dmg" "${tmpMountPoint}/AppleDiagnostics.chunklist" -diagmachineblacklist 0 -installbootfromtarget 0 -slurpappleboot 0 -delappleboot 0 -addkernelcoredump 0 else "${dmBin}" ensureRecoveryPartition "${gVolume}" "${tmpMountPoint}/BaseSystem.dmg" "${tmpMountPoint}/BaseSystem.chunklist" "${tmpMountPoint}/AppleDiagnostics.dmg" "${tmpMountPoint}/AppleDiagnostics.chunklist" 0 0 0 fi # done /usr/bin/hdiutil eject "${tmpMountPoint}" rm -rf "${tmpMountPoint}" rm -rf "${tmpRecUpdDir}" } function updatePrimarySystem() { printf "${CYAN}UPDATING PRIMARY SYSTEM${OFF}\n" cd "${gWorkDir}" # remove these lines to avoid installer error sed -e '/volume-check/d' "${gDist}" > new1.txt sed -e '/installation-check/d' new1.txt > new2.txt sed -e '/RecoveryHDUpdate/d' new2.txt > new3.txt sed -e '/system-image/d' new3.txt > new4.txt # remove macOS${gVersion}Patch.pkg since it will cause installation failure sed -e '/Patch/d' new4.txt > new5.txt cp new5.txt "${gDist}" rm new?.txt # build pkg productbuild --distribution "${gWorkDir}/${gDist}" --package-path "${gWorkDir}" "${gWorkDir}/${gInstallerPackage}" # perform the update by launching the installer [[ -f "${gWorkDir}/${gInstallerPackage}" ]] && /usr/sbin/installer -pkg "${gWorkDir}/${gInstallerPackage}" -target "${gVolume}" } function updateSystem() { # update Recovery first updateRecovery # then primary system updatePrimarySystem } function ask4Backup() { local confirmBackup read -p "Do you want to backup installer.pkg? (y/N) " confirmBackup case "${confirmBackup}" in y|Y ) printf "${CYAN}BACKING UP${OFF}\n" hdiutil create -format UDZO -srcfolder "${gWorkDir}/${gInstallerPackage}" "${gUserPath}/Installer_${gVersion}_${build}" ;; esac } function endProgram() { printf "${CYAN}CLEANING UP${OFF}\n" rm -rf "${gWorkDir}" rm -rf "${gTmpDirectory}/update-catalogs" rm -rf "${gTmpDirectory}/updRec" STY_LINE printf "${GREEN}RESTART REQUIRED${OFF}\n" local confirmReboot read -p "Do you want to reboot now? (y/N) " confirmReboot case "${confirmReboot}" in y|Y ) reboot ;; esac printf "${RED}ALL DONE${OFF}\nPlease reboot manually.\n" } function main() { updateScript "$@" # try to register the script registerScript # direct update from a pre-built pkg case "$1" in -i|--installer ) selectDestination local installerPKG read -p "Drag installer.pkg on terminal windows: " installerPKG [[ ! -f "${installerPKG}" ]] && echo "${installerPKG} not found!" && exit 1 printf "${CYAN}UPDATING PRIMARY SYSTEM${OFF}\n" /usr/sbin/installer -pkg "${installerPKG}" -target "${gVolume}" endProgram # done exit 0 ;; esac parseCatalog downloadPackage selectDestination updateSystem ask4Backup endProgram } clear if [[ $EUID -ne 0 ]]; then echo "$(basename "$0") must be run as ROOT!" sudo "$0" "$@" else main "$@" fi