Skip to content

Instantly share code, notes, and snippets.

Last active January 3, 2024 01:44
Show Gist options
  • Save SteveBenner/11254428 to your computer and use it in GitHub Desktop.
Save SteveBenner/11254428 to your computer and use it in GitHub Desktop.
Homebrew uninstall script
#!/usr/bin/env ruby
# CLI tool for locating and removing a Homebrew installation
# Copyright (C) 2014 Stephen C. Benner
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <>.
# Author: Stephen Benner
# Contributors:
# - @AaronKulick
# - @lloeki
# - @lewismc
# NOTE: This script has ONLY been tested and verified for the following operating systems:
# - macOS 10.9
# - v1.1: test deletion code, make sure it can ALWAYS remove
# - v1.2: handle ENOENT errors (file not found)
# - v1.3: find and remove daemons installed by brew
# - v1.4: figure out why script can sometimes loop through multiple `locate_brew_path` runs
# - v1.5: fix git repo detection code so it doesn't break when brew is checked out but not installed
# - v1.6: improve script to work even without a Homebrew installation
require 'optparse'
require 'fileutils'
require 'open3'
$stdout.sync = true
# Default options
options = {
:quiet => false,
:verbose => true,
:dry_run => false,
:force => false,
:find_path => false
optparser = do |opts|
opts.banner = 'Homebrew uninstaller' + $/
opts.on('-q', '--quiet', 'Quiet mode - suppress output.') do |setting|
options[:quiet] = setting
options[:verbose] = false
opts.on('-v', '--verbose', 'Verbose mode - print all operations.') { |setting| options[:verbose] = setting }
opts.on('-d', '--dry', 'Dry run - print results, but perform no actual operations.') do |setting|
options[:dry_run] = setting
opts.on('-f', '--force', 'Forces removal of files, bypassing prompt. USE WITH CAUTION!') do |setting|
options[:force] = setting
opts.on('-p', '--find-path', 'Output homebrew location if found, then exit.') do |setting|
options[:find_path] = setting
options[:quiet] = true
opts.on_tail('-h', '--help', '--usage', 'Display this message.') { puts opts; exit }
opts.on_tail('--version', 'Display script version.') { puts opts.version; exit }
optparser.version = '1.0'
optparser.summary_width = 16
$quiet = options[:quiet] # provides access to option value within methods
# Files installed into the Homebrew repository
# Files that Homebrew installs into other system locations
$files = []
# This function runs given command in a sub-shell, expecting the output to be the
# path of a Homebrew installation. If given a block, it passes the shell output to
# the block for processing, using the return value of the block as the new path.
# Known Homebrew files are then scanned for and added to the file list. Then the
# directory is tested for a Homebrew installation, and the git index is added if
# a valid repo is found. The function won't run once a Homebrew installation is
# found, but it will accumulate untracked Homebrew files each invocation.
# @param [String] cmd A shell command to run
# @param [String] error_msg Message to print if command fails
def locate_brew_path(cmd, error_msg = 'check homebrew installation and PATH.')
return if $brew_location # stop testing if we find a valid Homebrew installation
puts "Searching for homewbrew installation using '#{cmd}'..." unless $quiet
# Run given shell command along with any code passed-in via block
path = `#{cmd}`.chomp
path = yield(path) if block_given? # pass command output to your own fancy code block
Dir.chdir(path) do
# Search for known Homebrew files and folders, regardless of git presence
$files += { |file| File.exist? file }.map {|file| File.expand_path file }
$files += Dir.glob('**/{man,bin}/**/brew*')
# Test for Homebrew git repository (use popen3 so we can suppress git error output)
repo_name = Open3.popen3('git remote -v') do |stdin, stdout, stderr|
if repo_name =~ /homebrew.git|Homebrew/
$brew_location = path
rescue StandardError # on normal errors, continue program
# Attempt to locate homebrew installation using a command and optional code block
# for processing the command results. Locating a valid path halts searching.
# TODO: Improve this code - refactor and add error handling
locate_brew_path 'brew --prefix'
locate_brew_path('which brew') { |output| File.expand_path('../..', output) }
locate_brew_path('command -v brew') { |output| File.expand_path('../..', output) }
locate_brew_path 'brew --prefix' do |output|
output = output.split($/).first
File.expand_path('../..', output)
# Found Homebrew installation
if $brew_location
if options[:find_path]
puts $brew_location
unless options[:quiet]
puts "Homebrew found at: #{$brew_location}"
begin # record kegs and taps for later output
brewed = `brew list`
tapped = `brew tap`
rescue StandardError
# Collect files indexed by git
Dir.chdir($brew_location) do
# Update file list (use popen3 so we can suppress git error output)
Open3.popen3('git checkout master') { |stdin, stdout, stderr| stderr.close }
$files += `git ls-files` {|file| File.expand_path file }
rescue StandardError => e
puts e # Report any errors, but continue the script and collect any last files
# Collect any files Homebrew may have installed throughout our system
$files += { |file| File.exist? file }
abort 'Failed to locate any homebrew files!' if $files.empty?
unless options[:force]
print "Delete #{$files.count} files? "
abort unless gets.rstrip =~ /y|yes/i
rm =
if options[:dry_run]
lambda { |entry| puts "deleting #{entry}" unless options[:quiet] }
lambda { |entry| FileUtils.rm_rf(entry, :verbose => options[:verbose]) }
puts 'Deleting files...' unless options[:quiet]
# Print a list of formulae and kegs that were removed as part of the uninstall process
if brewed
puts 'The following previously installed formulae were removed:'
puts brewed
if tapped
puts 'The following previously tapped kegs were removed:'
puts tapped
Copy link

gethubd commented Aug 26, 2015

Hi Steve - thanks for the script. Just FYI I tried this on mac os x 10.6.8 and had the same problem as jproberts14. My guess is that the homebrew installs to different locations on 10.6 and the script doesn't see all of the files (however I am a complete novice so it's entirely just a guess). Your script didn't actually 'hang' as stated by jproberts14 - I was watching the activity in the activity monitor and it said ruby was very busy (with varying levels of cpu and disk activity) presumably looking for files. This took about 5 minutes before coming up with the "Delete 27 files ?" response. Most of the files it found were in the /local/ dir though it missed many others also in there.
I manually deleted all the remaining files in usr/local/ except for a few which were much older than the homebrew install.
Any tips on finding any other files to delete ( and I suspect there a a couple of thousand lurking around somewhere as between your script and manual deletion I think we only removed about 100-200), would be appreciated.
Thanks again for sharing.

Copy link

Hi Steve,
I experienced the same problem.

sudo ./unbrew.rb

Searching for homewbrew installation using 'brew --prefix'...
./unbrew.rb:105:in ``': No such file or directory - brew (Errno::ENOENT)
from ./unbrew.rb:105:inlocate_brew_path' from ./unbrew.rb:131:in


when I installed homebrew i quitted by control+C because of network problem. I do not know what should I do now....

use rm -rf /usr/local/Cellar /usr/local/.git I resolved problems, thanks~

Copy link

I tried Homebrew's uninstall command, which just messed up my installation. I couldn't use any of the packages afterward, nor could I reinstall them. They need to fix that! Your script cleaned out what needed to be cleaned, and made it so I could reinstall Homebrew from scratch. Thanks so much for posting this!

Copy link

lloeki commented Apr 11, 2016

Watch out, this script doesn't stop potentially running services beforehand, something to the effect of:

for d in {/Users/`whoami`,}/Library/Launch{Agents,Daemons}/homebrew.mxcl.*.plist; do launchctl unload $d; done

Copy link

Thank you very much. It works very well.

Copy link

lewismc commented Sep 20, 2016

@SteveBenner, if you have homebrew or homebrew-core already checked out to /usr/local then the script will fail, incorrectly thinking that you have Homebrew installed as oppose to just checked out locally into /usr/local.
After pruning both homebrew and homebrew-core from /usr/local and using your script it all went well thanks.

Copy link

@SteveBenner I ran the script but it is stuck at the message "Searching for homewbrew installation using 'brew --prefix'..." for over an hour. Thanks!

Searching for homewbrew installation using 'brew --prefix'...
Searching for homewbrew installation using 'which brew'...
Searching for homewbrew installation using 'command -v brew'...
Searching for homewbrew installation using 'brew --prefix'...

This is all I see

Copy link

Thanks for the effort and continued comments/support - worked flawlessly for me

Copy link

techartist commented Nov 26, 2016

@SteveBenner I am having the same issues as @cmfrtblynmb728. Any suggestions? No options were given on the command line I just ran ./unbrew.rb.

Copy link

Likewise, running on 10.10, needed to clear it out, seems to have worked exactly as intended. Much appreciated.

Copy link

anthonyrollett commented Dec 11, 2016

I should have said that I found I had to clear brew out because I still use gfortran from HPC Mac OS X ( and I needed to reinstall gcc and gfortran (versions 5.1). May not be the best approach these days, so happy to see further comments. Thanks again.

Copy link

I was trying to use your script to uninstall Homebrew from Mac OSX Mojave (10.14.5) as it was unable to find the latest Xcode and Command Line Tools. However, it seems that the script is not actually deleting the files, they remain even after I run it. I am attaching the terminal output for your reference:

Apple$ ./unbrew.rb 
Searching for homewbrew installation using 'brew --prefix'...
Searching for homewbrew installation using 'which brew'...
Searching for homewbrew installation using 'command -v brew'...
Searching for homewbrew installation using 'brew --prefix'...
Delete 16 files? y
Deleting files...
rm -rf share/man/man1/brew-cask.1
rm -rf share/man/man1/brew.1
rm -rf bin/brew
rm -rf Homebrew/bin/brew
rm -rf share/man/man1/brew-cask.1
rm -rf share/man/man1/brew.1
rm -rf bin/brew
rm -rf Homebrew/bin/brew
rm -rf share/man/man1/brew-cask.1
rm -rf share/man/man1/brew.1
rm -rf bin/brew
rm -rf Homebrew/bin/brew
rm -rf usr/local/share/man/man1/brew-cask.1
rm -rf usr/local/share/man/man1/brew.1
rm -rf usr/local/bin/brew
rm -rf usr/local/Homebrew/bin/brew
Apples-MacBook-Air:unbrew Apple$ cd 
Apples-MacBook-Air:~ Apple$ cd /usr/l
lib/     libexec/ local/   
Apples-MacBook-Air:~ Apple$ cd /usr/l
lib/     libexec/ local/   
Apples-MacBook-Air:~ Apple$ cd /usr/local/
Apples-MacBook-Air:local Apple$ ls
Caskroom	etc		lib		sbin		var
Homebrew	gfortran	opt		share
bin		include		remotedesktop	texlive

Can you please advise what I should do? I also used unbrew.rb with sudo.

Copy link

I have vestigial homebrew files, but homebrew hasn't been installed for years, so I'm looking for an uninstaller that will clean up the files without having to reinstall homebrew. Unfortunately, like the official uninstaller, this doesn't work unless homebrew is installed.

Copy link

SteveBenner commented Mar 18, 2022

2022 UPDATE:

I have added a list of fixes and improvements based on the last few years of feedback. Time to update the script! Thanks to the community for all the feedback and help testing this.

@lewismc, @lloeki these are great ideas and will be added as features

Copy link

@Roboji, @claudia1204, @cmfrtblynmb728, @techartist, @binchuri

The output for all of your cases is similar, in that I can see a couple different possible bugs causing the same kind of issue for all of you.

There are several fixes I am going to implement, they are documented at the top of the script. It will take some time to test, but I’m confident I will soon know why it broke.

It would be helpful for those experiencing hangups using unbrew to kill it with CTL-C or similar, and report any errors that show up.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment