Created
November 4, 2025 17:33
-
-
Save chrisfesler/c59e1e5670e9db326c7bd2be102a42a6 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env ruby | |
| require 'open3' | |
| SIGNALS = [ | |
| [15, 3], | |
| [2, 3], | |
| [1, 4], | |
| [9, 0] | |
| ] | |
| def i?(arg) | |
| arg.to_i != 0 | |
| end | |
| def capture_lines(*command) | |
| output, status = Open3.capture2(*command) | |
| return [] unless status.success? | |
| output.lines | |
| end | |
| def running?(pid) | |
| capture_lines('ps', '-p', pid.to_s).length >= 2 | |
| end | |
| def go_ahead? | |
| response = $stdin.gets | |
| return false unless response | |
| %w(y yes yas).include? response.strip.downcase | |
| end | |
| def kill(pid, code) | |
| Process.kill(code.to_i, pid.to_i) | |
| rescue Errno::ESRCH, Errno::EPERM | |
| false | |
| end | |
| def murder_pid(pid) | |
| SIGNALS.each do |signal| | |
| break unless running? pid | |
| code, wait = signal | |
| kill(pid, code) | |
| sleep 0.5 | |
| sleep(wait) if running? pid | |
| end | |
| end | |
| def murder_names(name) | |
| regex = /\b#{Regexp.escape(name)}\b/i | |
| loop do | |
| should_loop = false | |
| running = capture_lines('ps', '-eo', 'pid=,command=') | |
| running.each do |line| | |
| pid, fullname = line.strip.split(nil, 2) | |
| next unless pid && fullname | |
| next unless regex.match?(fullname) | |
| next if Process.pid == pid.to_i | |
| print "murder #{fullname.chomp} (pid #{pid})? " | |
| if go_ahead? | |
| murder_pid(pid) | |
| should_loop = true | |
| break | |
| end | |
| end | |
| break unless should_loop | |
| end | |
| end | |
| def murder_port(arg) | |
| loop do | |
| should_loop = false | |
| lsofs = capture_lines('lsof', '-i', arg.to_s) | |
| lsofs.drop(1).each do |line| | |
| pid = line.split(nil, 3)[1] | |
| next unless pid | |
| fullname = capture_lines('ps', '-p', pid.to_s, '-o', 'command=').first | |
| fullname = fullname ? fullname.chomp : '<unknown>' | |
| print "murder #{fullname} (pid #{pid})? " | |
| if go_ahead? | |
| murder_pid(pid) | |
| should_loop = true | |
| break | |
| end | |
| end | |
| break unless should_loop | |
| end | |
| end | |
| def murder(arg) | |
| is_pid = i?(arg) | |
| is_port = arg[0] == ':' && i?(arg.slice(1, arg.size)) | |
| if is_pid | |
| murder_pid arg | |
| elsif is_port | |
| murder_port arg | |
| else | |
| murder_names arg | |
| end | |
| end | |
| if ARGV.size < 1 | |
| puts 'usage:' | |
| puts 'murder 123 # kill by pid' | |
| puts 'murder ruby # kill by process name' | |
| puts 'murder :3000 # kill by port' | |
| exit 1 | |
| else | |
| ARGV.each { |arg| murder(arg) } | |
| end |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Kanged from https://codeberg.org/EvanHahn/dotfiles.git