Autor: rtrouton
#!/bin/bash # This script uses a macOS 10.13 and later installer application to repair the Recovery volume (APFS) or partition (HFS+). # # Based on the following script: # https://gist.github.com/jonathantneal/f20e6f3e03d5637f983f8543df70cef5 # Provide custom colors in Terminal for status and error messages msg_status() { echo -e "\033[0;32m-- $1\033[0m" } msg_error() { echo -e "\033[0;31m-- $1\033[0m" } # Explanation of how the script works usage() { cat <<EOF Usage: $(basename "$0") "/path/to/Install macOS [Name].app" Description: This script uses a macOS 10.13 and later installer application to repair the Recovery volume (APFS) or partition (HFS+). Requirements: Compatible macOS installer application for the Mac's installed OS. Account with the ability to run commands using sudo, to allow specific functions to run with root privileges. EOF } # Set the macOS installer path as a variable. macOS_installer="$1" mount_point="$macOS_installer/Contents/SharedSupport" # Remove trailing slashes from input paths if needed macOS_installer=${macOS_installer%%/} # Check to see if the path to the macOS installer application has been provided # as part of running the script. if [[ -z "$1" ]] || [[ ! -d "$1" ]]; then msg_error "The path to the macOS installer application is required as the first argument." usage exit 1 else msg_status "macOS installer is $macOS_installer" fi # Check for the OS information of both the macOS installer application # and that of the Mac this script is running on. macos_version=$(/usr/bin/sw_vers -productVersion) macos_version_digits_only=$(echo "$macos_version" | awk -F'[^0-9]*' '$0=$1$2$3') installer_version=$(/usr/libexec/PlistBuddy -c 'Print :System\ Image\ Info:version' "$macOS_installer/Contents/SharedSupport/InstallInfo.plist") installer_version_digits_only=$(echo $installer_version | awk -F'[^0-9]*' '$0=$1$2$3') installer_qualifies=$(echo $installer_version_digits_only | head -c4) installer_mounted_volume=$(echo "$macOS_installer" | grep -o 'Install.*' | sed 's/....$//') # If the installed OS is only four digits long, add a zero to the end. if [[ $(echo -n "$macos_version_digits_only" | wc -m | tr -d '[:space:]') = "4" ]]; then macos_version_digits_only="$macos_version_digits_only"0 fi # If the installer's OS version is only four digits long, add a zero to the end. if [[ $(echo -n "$installer_version_digits_only" | wc -m | tr -d '[:space:]') = "4" ]]; then installer_version_digits_only="$installer_version_digits_only"0 fi # Define download URL and name for the RecoveryHDUpdate installer package # which includes the tools needed to rebuild the Recovery volume or partition. recovery_download="http://swcdn.apple.com/content/downloads/63/02/061-90748-A_MQ4MTMMXYW/jtw6sxlorttnetfsu9wpu0rqubsa55bskp/SecUpd2020-002HighSierra.RecoveryHDUpdate.pkg" recovery_package="SecUpd2020-002HighSierra.RecoveryHDUpdate.pkg" # Detect if the macOS installer application is running macOS 10.13.x or later. If the # macOS installer application is for 10.12.x or earlier, stop and display an error message. if [[ "$installer_qualifies" -lt 1013 ]]; then msg_error "This script supports repairing Recovery for macOS 10.13.0 and later." msg_error "Please use an installer app which installs macOS 10.13.0 or later." exit 1 else msg_status "Installer application for macOS $installer_version detected. Proceeding...." fi if [[ "$installer_version_digits_only" -lt "$macos_version_digits_only" ]]; then msg_error "The macOS installer app provided installs $installer_version's Recovery, which is earlier than macOS $macos_version" msg_error "Please use an installer app which installs macOS $macos_version or later." usage exit 1 fi # Identify the target disk boot_drive=$(/usr/sbin/diskutil info "$(bless --info --getBoot)" | awk -F':' '/Volume Name/ { print $2 }' | sed -e 's/^[[:space:]]*//') msg_status "Target disk is ${boot_drive}." # Detect the target disk's filesystem type (APFS or HFS+) filesystem_type=$(/usr/sbin/diskutil info "$boot_drive" | awk '$1 == "Type" { print $NF }') msg_status "Target filesystem is ${filesystem_type}." msg_status "Downloading $recovery_package into /private/tmp" /usr/bin/curl "$recovery_download" --progress-bar -L -o /private/tmp/"$recovery_package" pkgutil --expand /private/tmp/"$recovery_package" /private/tmp/recoveryupdate"$installer_version" if [[ -d /private/tmp/recoveryupdate"$installer_version" ]]; then return_code=0 if [[ "${filesystem_type}" == "apfs" ]]; then msg_status "Running ensureRecoveryBooter for APFS target volume: ${boot_drive}" /private/tmp/recoveryupdate"$installer_version"/Scripts/Tools/dm ensureRecoveryBooter "$boot_drive" -base "$mount_point/BaseSystem.dmg" "$mount_point/BaseSystem.chunklist" -diag "$mount_point/AppleDiagnostics.dmg" "$mount_point/AppleDiagnostics.chunklist" -diagmachineblacklist 0 -installbootfromtarget 0 -slurpappleboot 0 -delappleboot 0 -addkernelcoredump 0 return_code=$(($return_code + $?)) if [[ "$return_code" == 0 ]]; then msg_status "Successfully created $installer_version Recovery volume on ${boot_drive}." else msg_error "Failed to create $installer_version Recovery volume on ${boot_drive}." msg_error "Recovery tools returned the following non-zero exit code: $return_code" exit $return_code fi elif [[ "${filesystem_type}" == "hfs" ]]; then msg_status "Running ensureRecoveryPartition for non-APFS target volume: ${boot_drive}" /private/tmp/recoveryupdate"$installer_version"/Scripts/Tools/dm ensureRecoveryPartition "$boot_drive" "$mount_point/BaseSystem.dmg" "$mount_point/BaseSystem.chunklist" "$mount_point/AppleDiagnostics.dmg" "$mount_point/AppleDiagnostics.chunklist" 0 0 0 return_code=$(($return_code + $?)) if [[ "$return_code" == 0 ]]; then msg_status "Successfully created $installer_version Recovery partition on ${boot_drive}." else msg_error "Failed to create $installer_version Recovery partition on ${boot_drive}." msg_error "Recovery tools returned the following non-zero exit code: $return_code" exit $return_code fi else msg_error "Failed to create $installer_version Recovery partition on ${boot_drive}." msg_error "${boot_drive} does not have an APFS or HFS+ filesystem." fi else msg_error "Unable to locate the following directory: /private/tmp/recoveryupdate"$installer_version"" exit 1 fi # Clean up /bin/rm -rf /private/tmp/"$recovery_package" /bin/rm -rf /private/tmp/recoveryupdate"$installer_version"