Remove homebrew install dotnet sdk and optional related directories.
#!/usr/bin/env bash
#
# remove-homebrew-dotnet.sh
#
# Safe to rerun. Set PURGE_ALL_DOTNET=1 for a complete machine-wide .NET purge.
#
# # To install again, run:
# $ brew update
# $ brew install --cask dotnet-sdk
#
set -o pipefail
PURGE_ALL_DOTNET="${PURGE_ALL_DOTNET:-0}"
case "$PURGE_ALL_DOTNET" in
0|1) ;;
*) echo "PURGE_ALL_DOTNET must be 0 or 1."; exit 2 ;;
esac
contains() {
local needle="$1"
shift
local item
for item in "$@"; do
[[ "$item" == "$needle" ]] && return 0
done
return 1
}
formulae=()
casks=()
dependencies=()
BREW=""
if command -v brew >/dev/null 2>&1; then
BREW="$(command -v brew)"
elif [[ -x /opt/homebrew/bin/brew ]]; then
BREW="/opt/homebrew/bin/brew"
fi
if [[ -n "$BREW" ]]; then
if ! "$BREW" list --formula >/dev/null 2>&1 ||
! "$BREW" list --cask >/dev/null 2>&1; then
echo "Homebrew could not list installed packages."
exit 1
fi
while IFS= read -r name; do
case "$name" in
dotnet|dotnet@*) formulae+=("$name") ;;
esac
done < <("$BREW" list --formula)
while IFS= read -r name; do
case "$name" in
dotnet|dotnet@*|dotnet-*) casks+=("$name") ;;
esac
done < <("$BREW" list --cask)
# Record the installed dependency tree before removing the .NET formulae.
for formula in "${formulae[@]}"; do
deps_text="$("$BREW" deps --formula --installed --recursive "$formula")" || exit 1
while IFS= read -r dependency; do
[[ -z "$dependency" ]] && continue
contains "$dependency" "${dependencies[@]}" || dependencies+=("$dependency")
done <<< "$deps_text"
done
failed=0
if ((${#formulae[@]})); then
printf 'Removing Homebrew formulae: %s\n' "${formulae[*]}"
"$BREW" uninstall --formula --force "${formulae[@]}" || failed=1
fi
if ((${#casks[@]})); then
printf 'Removing Homebrew casks: %s\n' "${casks[*]}"
"$BREW" uninstall --cask --force --zap "${casks[@]}" || failed=1
fi
if ((failed)); then
echo "A Homebrew uninstall failed; no manual cleanup was performed."
exit 1
fi
# Remove only former .NET dependencies that are now unused and were
# installed as dependencies---not packages you explicitly installed.
while :; do
leaves=()
leaves_text="$("$BREW" leaves --installed-as-dependency)" || exit 1
while IFS= read -r leaf; do
[[ -n "$leaf" ]] && leaves+=("$leaf")
done <<< "$leaves_text"
removed_this_pass=0
for dependency in "${dependencies[@]}"; do
if contains "$dependency" "${leaves[@]}"; then
echo "Removing now-unused Homebrew dependency: $dependency"
"$BREW" uninstall --formula "$dependency" || exit 1
removed_this_pass=1
fi
done
((removed_this_pass)) || break
done
# Remove any Homebrew-owned residual directories and links for the
# packages just removed.
cellar="$("$BREW" --cellar)"
caskroom="$("$BREW" --caskroom)"
prefix="$("$BREW" --prefix)"
for formula in "${formulae[@]}"; do
[[ -d "$cellar/$formula" ]] && rm -rf "$cellar/$formula"
[[ -L "$prefix/opt/$formula" ]] && rm -f "$prefix/opt/$formula"
done
for cask in "${casks[@]}"; do
[[ -d "$caskroom/$cask" ]] && rm -rf "$caskroom/$cask"
done
for link in "$prefix/bin/dotnet" "$prefix/bin/dnx"; do
if [[ -L "$link" ]] && [[ "$(readlink "$link")" == *dotnet* ]]; then
rm -f "$link"
fi
done
# Delete only Homebrew cache files whose filename identifies them as .NET.
cache_dir="$("$BREW" --cache)"
if [[ -d "$cache_dir" ]]; then
while IFS= read -r -d '' cache_file; do
rm -f "$cache_file"
done < <(find "$cache_dir" -type f -iname '*dotnet*' -print0)
fi
else
echo "Homebrew was not found; skipping Homebrew package cleanup."
fi
# Remove stale Homebrew-specific .NET lines from common shell profiles.
# A one-time backup is made before each changed profile is updated.
for profile in "$HOME/.zprofile" "$HOME/.zshrc" "$HOME/.zlogin" \
"$HOME/.bash_profile" "$HOME/.bashrc" "$HOME/.profile"; do
[[ -f "$profile" ]] || continue
tmp="$(mktemp "${TMPDIR:-/tmp}/dotnet-profile.XXXXXX")" || exit 1
if [[ "$PURGE_ALL_DOTNET" == "1" ]]; then
awk '
/HOMEBREW_PREFIX.*dotnet/ ||
/brew[[:space:]]+--prefix[[:space:]]+dotnet/ ||
/\/(opt\/homebrew|usr\/local)\/(opt|Cellar)\/dotnet/ ||
/DOTNET_ROOT/ ||
/PATH=.*(\.dotnet|\/usr\/local\/share\/dotnet)/ { next }
{ print }
' "$profile" > "$tmp"
else
awk '
/HOMEBREW_PREFIX.*dotnet/ ||
/brew[[:space:]]+--prefix[[:space:]]+dotnet/ ||
/\/(opt\/homebrew|usr\/local)\/(opt|Cellar)\/dotnet/ { next }
{ print }
' "$profile" > "$tmp"
fi
if ! cmp -s "$profile" "$tmp"; then
backup="${profile}.before-dotnet-cleanup.bak"
[[ -e "$backup" ]] || cp -p "$profile" "$backup"
mv "$tmp" "$profile"
echo "Updated $profile (backup: $backup)"
else
rm -f "$tmp"
fi
done
if [[ "$PURGE_ALL_DOTNET" == "1" ]]; then
echo "Purging all remaining system and user .NET data."
echo "This removes non-Homebrew .NET SDKs/runtimes, global tools, NuGet caches, and config."
sudo -v || exit 1
# Remove remaining Microsoft .NET installer receipts.
while IFS= read -r receipt; do
sudo pkgutil --forget "$receipt" >/dev/null || true
done < <(
pkgutil --pkgs |
awk '/^com\.microsoft\.(dotnet|netstandard)\./ { print }'
)
# Includes Arm64 .NET and the Rosetta x64 subtree on Apple Silicon.
sudo rm -rf /usr/local/share/dotnet
sudo rm -f /etc/paths.d/dotnet /etc/paths.d/dotnet-cli-tools
rm -rf \
"$HOME/.dotnet" \
"$HOME/.nuget" \
"$HOME/Library/Caches/NuGet" \
"$HOME/Library/Application Support/NuGet"
fi
hash -r
echo "Done. Open a new Terminal window before checking PATH."