2014-04-07 15 views

回答

3

http://hints.macworld.com/article.php?story=20071030151739791是一個很好的起點,但已經過時了。

基於此,我爲OS X 10.9編寫了bash腳本。

顯然,使用您自己的風險 - 這是不經過戰鬥測試。

  • 將其另存爲例如resetMac並使其可執行chmod +x
  • 使用-h調用命令行幫助。
  • 它被設計成運行在作爲root單用戶模式 - 儘管它也可能通過ssh進行遠程操作,假定沒有用戶交互式登錄。
  • ,看看它 DO(一預演),與-t調用它 - 你可以做到這一點,即使是root沒有運行。
  • 有一些檢查可確保腳本以root的形式運行,並且存在確認提示,需要仔細輸入才能繼續。
  • 如果發現啓動驅動器的名稱不同,則啓動驅動器的名稱將重置爲「Macintosh HD」。
  • 刪除所有「真實」用戶及其家庭文件夾 - Guest用戶(以及任何幕後用戶,如rootdaemon)除外。
  • 可選地,可以執行安全擦除(產生的)可用磁盤空間。
  • 設置運行指示器文件被刪除,以確保初始設置過程在下次啓動時運行。
  • /Library/Preferences/SystemConfiguration/的內容已刪除,如原始腳本中的內容(不完全確定其後果是什麼)。
  • 不確定是否有任何步驟丟失。
  • 最後有2行 - 當前註釋爲OUT - 會在完成時自行刪除腳本 - 根據需要激活。

bash腳本resetMac

#!/bin/bash 

# Gratefully adapted from http://hints.macworld.com/article.php?story=20071030151739791 

DESIGNED_FOR_OSX_VERSION='10.9' 
DEFAULT_BOOTDISK_NAME='Macintosh HD' 

# ---- BEGIN: Helper functions 
    # Helper function for exiting in case of error. 
die() { echo "$0: ERROR: ${1:-"ABORTING due to unexpected error."}" 1>&2; exit ${2:-1}; } 

# SYNOPSIS 
#  indexOf needle "${haystack[@]}" 
# *Via stdout*, returns the zero-based index of a string element in an array of strings or -1, if not found. 
# The *return code* indicates if the element was found or not. 
# EXAMPLE 
# a=('one' 'two' 'three') 
# ndx=$(indexOf 'two' "${a[@]}") # -> $ndx is now 1 
indexOf() { 
    local e ndx=-1 
    for e in "${@:2}"; do ((++ndx)); [[ "$e" == "$1" ]] && echo $ndx && return 0; done 
    echo '-1' 
    return 1 
} 
# ---- END: Helper functions 

# Command-line help. 
if [[ $1 == '--help' || $1 == '-h' ]]; then 
    cat <<EOF 

SYNOPSIS 
    $(basename "$0") [-t] 

DESCRIPTION 

    RESETS THIS MAC: 

    - All "REAL" USERS will be DELETED. 
    - All THEIR USER FILES will be DELETED. 
    - The boot disk will be renamed to 
    "$DEFAULT_BOOTDISK_NAME", if necessary. 
    - The CONFIGURATION in 
     /Library/Preferences/SystemConfiguration/ 
    will be DELETED, which includes the COMPUTER NAME/
    HOST NAME and NETWORK INFORMATION. 
    - OPTIONALLY, free space can afterwards be 
    SECURELY ERASED. 
    - The next time you boot, INITIAL SETUP will 
    RUN AGAIN. 

    NOTE: DESIGNED FOR OS X $DESIGNED_FOR_OSX_VERSION. 
     IT IS LIKELY THAT THIS SCRIPT NEEDS TWEAKING 
     TO RUN PROPERLY ON OTHER VERSIONS. 

    Must be run in SINGLE-USER mode. 
    Alternatively, run as ROOT via SSH from another 
    machine while no one is logged in interactively. 

    "Real" users refers to user accounts representing 
    real people - which does NOT include \`Guest\` or \`root\`. 
    In essence: 
    - the user created during initial setup 
    - any additional users, if any, created via 
    \`System Preferences > Users & Groups\`. 

    A confirmation prompt is always shown. 

    -t ... performs a TEST (DRY RUN) ONLY and simply ECHOES 
    THE COMMANDS that would be performed, without executing 
    them. In this case even a non-root user can run the script. 

TIPS 
    Additional things to do or check for, preferably 
    BEFORE running this script: 

    - Use a tool like OnyX (http://www.titanium.free.fr/downloadonyx.php) 
    to clean all caches. 

EOF 
    exit 0 
fi 

dryRun=0 
[[ $1 == '-t' ]] && { dryRun=1; shift; } 
(($# == 0)) || die "Unexpected number of arguments; use -h for help." 2 

    # Ensure that the script is being run as root - unless it's a dry run. 
[[ $(id -u) == 0 || $dryRun == 1 ]] || die "This script must be run as ROOT." 

    # Issue warning, if the current OS X version doesn't match the 
    # one this script was designed for. 
thisOsxVersion=$(sw_vers -productVersion | cut -d. -f 1-2) 
[[ $thisOsxVersion != $DESIGNED_FOR_OSX_VERSION ]] && printf '!!!!!\nWARNING: This script is DESIGNED FOR %s, but YOU ARE RUNNING %s.\n!!!!!\n' $DESIGNED_FOR_OSX_VERSION $thisOsxVersion 

    # DRY RUN MODE: 
    # Determine the command prefix. 
prefix='echo [DRY RUN]' 
((dryRun)) || prefix='' 
    # Determine dry-run-mode hint. 
dryRunHint='---------------- 
DRY RUN ONLY: NO CHANGES WILL BE MADE 
----------------' 
((dryRun)) || dryRunHint='' 

cat <<EOF 
---------------- 

CAUTION: This script RESETS YOUR MAC: 

- All "REAL" USERS will be DELETED 
- All THEIR USER FILES will be DELETED. 
- OPTIONALLY, FREE DISK SPACE is afterwards 
    SECURELY ERASED. 
- The boot disk will be renamed to 
    "$DEFAULT_BOOTDISK_NAME", if necessary. 
- The CONFIGURATION in 
    /Library/Preferences/SystemConfiguration/ 
    will be DELETED, which inludes the COMPUTER 
    NAME/HOST NAME and NETWORK INFORMATION. 
- The next time you boot, INITIAL SETUP will 
    RUN AGAIN. 

    Must be run in SINGLE-USER mode. 
    Alternatively, run as ROOT via SSH from 
    another machine WHILE NO ONE IS LOGGED IN 
    INTERACTIVELY. 

RECOMMENDATION: Before running this script, 
    use a tool like OnyX 
    (http://www.titanium.free.fr/downloadonyx.php) 
    to clean all caches. 

---------------- 
EOF 

[[ -n $dryRunHint ]] && echo "$dryRunHint" 

# Ask for secure erasing. 
cat <<EOF 
OPTIONAL: 
    Do you want to SECURELY ERASE the FREE DRIVE SPACE 
    after deleting data? 
    Caution: This can take a LOOONG time; 
      6) is a reasonable compromise between security 
      and length of the operation. 
TYPE A NUMBER: 
EOF 
secureErase=0 lvl= 
    # Except for the bookending options, these were taken from and *must match* the erasure methods listed by `diskutil secureErase`. 
choices=('NO, THANKS' 'single-pass with zeros' 'single-pass with random data' 'US DoD 7-pass' 'Gutmann 35-pass' 'US DoE 3-pass' 'ABORT') 
select eraseMethod in "${choices[@]}"; do 

    # Determine the 0-based index of the selection in the array of choices 
    ndx=$(indexOf "$eraseMethod" "${choices[@]}") 

    case $ndx in 
    -1) # invalid input 
     echo "Invalid input. Please type the number next to the desired option." >&2 
     continue 
     ;; 
    0) # skip and continue 
     echo "(Resulting free space will NOT be securely erased.)" 
     break 
     ;; 
    $((${#choices[@]} - 1))) # abort 
     die "ABORTED." 
     ;;  
    *) # specific level chosen 
     secureErase=1 
     lvl=$((ndx - 1)) # map to the corresponding `diskutil secureErase` level 
     echo "Resulting free space will be SECURELY ERASED using method $eraseMethod." 
     break 
     ;; 
    esac 

done 

[[ -n $dryRunHint ]] && echo "$dryRunHint" 

    # Confirmation prompt. 
echo "Type 'I UNDERSTAND.' to proceed. DATA WILL BE IRRETRIEVABLY DELETED. To ABORT, just press Enter." 
printf '> ' 
read 
[[ $REPLY == 'I UNDERSTAND.' ]] || { echo 'Aborted.' 1>&2; exit 1; } 


    # IF IN SINGLE-USER MODE : Check and mount the hard drive so that we can write. 
    # Note: We detect whether we're running in single-user mode by whether the $HOME variable is empty. 
if [[ -z $HOME || $dryRun == 1 ]]; then 
    $prefix /sbin/fsck -fy 
    $prefix /sbin/mount -uw/|| die 
fi 

echo "Loading required system daemons..." 

# Load the services that are needed to run `diskutil` - thanks http://www.system-fabrik.de/diskutil-single-user-mode/ 
# !! This may asynchronously output some Ethernet-related status messages, which can be ignored. 
# !! Also, using `diskutil` later will output warnings related to the "distnoded server", but they can be 
# !! ignored and don't affect the exit code. 
$prefix launchctl load /System/Library/LaunchDaemons/com.apple.notifyd.plist || die 
$prefix launchctl load /System/Library/LaunchDaemons/com.apple.configd.plist || die 
$prefix launchctl load /System/Library/LaunchDaemons/com.apple.diskmanagementd.plist || die 
$prefix launchctl load /System/Library/LaunchDaemons/com.apple.securityd.plist || die 
$prefix launchctl load /System/Library/LaunchDaemons/com.apple.diskarbitrationd.plist || die 

# Load Directory Services. 
# !! As of 10.9; note that earlier versions used a different .plist file: 
# !! .../com.apple.DirectoryServices.plist 
# !! Unless loading succeeds, the `dscl` utility will implicitly try to 
# !! load yet another OBSOLETE .plist file: 
# !! .../com.apple.DirectoryServicesLocal.plist 
$prefix launchctl load /System/Library/LaunchDaemons/com.apple.opendirectoryd.plist || die 

# Test for need to rename the boot disk back to the default name. 
bootDiskName=$(diskutil info/| awk -F': +' '/ Volume Name:/ { print $2 }') 
if [[ $bootDiskName != $DEFAULT_BOOTDISK_NAME ]]; then 
    echo "Renaming boot disk from [$bootDiskName] back to default, [$DEFAULT_BOOTDISK_NAME]"... 
    $prefix diskutil rename/"$DEFAULT_BOOTDISK_NAME" || die 
else 
    echo "(Boot disk already has default name, [$DEFAULT_BOOTDISK_NAME].)" 
fi 

echo "Determining what users to delete..." 

# Determine all "real" (real-people) usernames, i.e.: 
# - The one created during initial setup. 
# - Additional ones created via System Preferences > Users & Groups. 
# - However: The 'Guest' user is NOT included. 
# (All other users, such as root, daemon, ... are ignored.) 
# Note: This loop is relatively slow. 
realUsrs=() 
realUsrHomeDirs=() 
for usr in $(dscl . -list /Users); do 
    if [[ $usr != 'Guest' ]]; then # Exclude the 'Guest' user. 
    # We identify real users by whether their home directory paths start with '/Users/' 
    homeDir=$(dscl . -read /Users/$usr NFSHomeDirectory | awk '{print $2}') || die 
    [[ $homeDir == /Users/* ]] && { realUsrs+=($usr); realUsrHomeDirs+=("$homeDir"); } 
    fi 
done 

((${#realUsrs[@]} > 0)) || die "Failed to determine usernames of regular users." 

i=0 
for usr in "${realUsrs[@]}"; do 

    homeDir=${realUsrHomeDirs[i++]} 

    echo " Deleting user '$usr' and his/her files..." 

    # Delete user's Directory Services group memberships. 
    for grp in $(dscl . -search /groups GroupMembership $usr | awk 'NF>1 {print $1}'); do 
    $prefix dscl . -delete /Groups/$grp GroupMembership $usr || die 
    done 

    # Delete user itself. 
    $prefix dscl . -delete /users/$usr || die 

    # Delete user's home folder. 
    $prefix rm -rf "$homeDir" || die 

done 

if ((secureErase)); then 

    echo "Erasing free disk space securely using method $eraseMethod; this can take a LOOONG time..." 

    # Determine the ID of the disk hosting/(the root of the filesystem) 
    diskId=$(df/| awk -F '[ /]' '/^\// { print $3 }') 
    [[ -n $diskId ]] || die "Could not determine disk ID for free-space erasing." 

    # CLI help from diskutil secureErase 
    # Usage: diskutil secureErase [freespace] level MountPoint|DiskIdentifier|DeviceNode 
    # Securely erases either a whole disk or a volume's freespace. 
    # Level should be one of the following: 
    #   0 - Single-pass zeros. 
    #   1 - Single-pass random numbers. 
    #   2 - US DoD 7-pass secure erase. 
    #   3 - Gutmann algorithm 35-pass secure erase. 
    #   4 - US DoE 3-pass secure erase. 
    # Ownership of the affected disk is required. 
    # Note: Level 2, 3, or 4 secure erases can take an extremely long time. 

    $prefix diskutil secureErase freespace $lvl $diskId || die 

fi 

echo "Removing setup-was-run file..." 
    # Remove the (empty) file that signals that 
    # the initial setup process has run. 
$prefix rm /var/db/.AppleSetupDone || die 

echo "Removing configuration files..." 
    # Remove configuration files (network), .... 
$prefix rm -rf /Library/Preferences/SystemConfiguration/* || die 

# Activate the following 2 lines if you want this script to SELF-DELETE. 
# echo "Deleting this script..." 
# $prefix rm "$0" 

cat <<EOF 
--------- 
RESET COMPLETED. 

Do you want to REBOOT NOW and START THE SETUP PROCESS (y/N)? 
--------- 
EOF 
printf '> ' 
read -r 
[[ $REPLY =~ ^[yY]$ ]] || exit 0 

    # Reboot 
echo "Initiating reboot..." 
$prefix shutdown -r now 
相關問題