我将如何为 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运行,并且有一个确认提示,需要故意输入才能继续。
  • 如果发现启动驱动器的名称不同,则会重置为"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

最新更新