2
我在想如何編寫一個腳本來刪除運行OS X 10.9的機器上的所有用戶/數據並重新啓動安裝助手。任何幫助將是偉大的,謝謝!如何爲OS X 10.9編寫一個腳本來刪除所有用戶並重新啓動初始設置?
編輯:我在一家維修店工作,我們經常設置測試用戶來檢查維修是否成功。如果我們可以移除這個測試用戶而不是每次都要執行擦除和安裝,那將會很好。
我在想如何編寫一個腳本來刪除運行OS X 10.9的機器上的所有用戶/數據並重新啓動安裝助手。任何幫助將是偉大的,謝謝!如何爲OS X 10.9編寫一個腳本來刪除所有用戶並重新啓動初始設置?
編輯:我在一家維修店工作,我們經常設置測試用戶來檢查維修是否成功。如果我們可以移除這個測試用戶而不是每次都要執行擦除和安裝,那將會很好。
http://hints.macworld.com/article.php?story=20071030151739791是一個很好的起點,但已經過時了。
基於此,我爲OS X 10.9
編寫了bash
腳本。
顯然,使用您自己的風險 - 這是不經過戰鬥測試。
resetMac
並使其可執行chmod +x
。-h
調用命令行幫助。root
單用戶模式 - 儘管它也可能通過ssh
進行遠程操作,假定沒有用戶交互式登錄。-t
調用它 - 你可以做到這一點,即使是root
沒有運行。root
的形式運行,並且存在確認提示,需要仔細輸入才能繼續。Guest
用戶(以及任何幕後用戶,如root
和daemon
)除外。/Library/Preferences/SystemConfiguration/
的內容已刪除,如原始腳本中的內容(不完全確定其後果是什麼)。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