Skip to content

Instantly share code, notes, and snippets.

@epitron
Created January 31, 2015 05:40
Show Gist options
  • Save epitron/d5676737950342a8a8cb to your computer and use it in GitHub Desktop.
Save epitron/d5676737950342a8a8cb to your computer and use it in GitHub Desktop.
A Crystal version of "f" (the file find utility)
#!/usr/bin/env ruby
#################################################################
## For author info, scroll to the end of this file.
#################################################################
#################################################################
## Load Modules
require "pp"
#################################################################
#################################################################
## Globals
if ARGV.any?{|arg| arg == "-v"}
ARGV.remove_if{|arg| arg == "-v"}
$verbose = true
else
$verbose = false
end
#################################################################
#################################################################
## Display Help (if requested)
if ["--help", "-h"].include?(ARGV[0])
puts DATA.read
exit
end
#################################################################
#################################################################
#################################################################
## NEW path scanner
def slashed(path)
path[-1] == '/' ? path : (path + "/")
end
def listdir(root)
root = slashed(root)
dirs = Dir.glob("#{root}*/", File::FNM_DOTMATCH)
files = Dir.glob("#{root}*", File::FNM_DOTMATCH)
dirs_without_slashes = dirs.map{|dir| dir[0...-1]}
files = files - dirs_without_slashes # remove dirs from file list
# drop the "." and ".." dirs
dirs = dirs.select { |dir| not dir =~ %r[/\.{1,2}/] }
# strip #{root} from paths
dirs, files = [dirs,files].map do |list|
list.map { |f| f[root.size..-1] }
end
[dirs, files]
end
$visited = {} of String => String # visited paths, to avoid symlink-loops
def breadth_first_scan(root, &block)
puts "=== #{root} ===" if $verbose
root = slashed(root)
dirs, files = listdir(root)
path_id = File.lstat(root).ino
if seenpath = $visited[path_id]
STDERR.puts "*** WARNING: Already seen #{root.inspect} as #{seenpath.inspect}" if $verbose
else
$visited[path_id] = root
dirs.each { |f| yield root, f }
files.each { |f| yield root, f }
for dir in dirs
breadth_first_scan(root+dir, &block)
end
end
end
#################################################################
struct String
def highlight(query)
self
end
end
#################################################################
## MAIN
if $0 == __FILE__
# Separate options from commmandline args
opts = ARGV.select{|arg| arg =~ /^-\w$/}
args = ARGV - opts
# Handle args
case args.size
when 0
query = ""
roots = ["."]
when 1
if args.first =~ %r{(^/|/$|^\./)} #and File.directory?(ARGV.first)
query = ""
roots = [args.first]
else
query = args.first
roots = ["."]
end
else
query = args.shift
roots = args
end
# Handle one-letter options (eg: -a)
for opt in opts
case opt
when "-a"
roots.unshift query
query = ""
when "-v"
$verbose = true
end
end
# Matcher
orig_query = query
query = Regexp.new( Regexp.escape( query ), Regexp::IGNORECASE )
# Ignore bad path arguments
roots = roots.select do |path|
File.exists?(path) || STDERR.puts("Error: #{path} doesn't exist")
end
# Search!
lesspipe(:wrap=>true) do |less|
roots.each do |root|
begin
breadth_first_scan(root) do |dirname, filename|
if orig_query['/']
# search the full path if the user put a '/' in the query
path = dirname + filename
less.puts path.highlight(query) if path =~ query
else
# search in the filenames only
less.puts dirname+filename.highlight(query) if filename =~ query
end
end
rescue Interrupt
# eat ^C's
exit(1)
end
end
end
end
#################################################################
#################################################################
## Help message (will be put in the DATA array)
__END__
"f" (c) 2002-2011 by Chris Gahan ([email protected])
Usage:
f => recursively list all files in current directory
f <search string> => recursively list all files in current directory
containing <search string>
f <search string> <paths> => recursively list all files in <paths>
containing <search string>
f -a <paths> => show all files in paths
f -v [...] => verbose mode (warns if there are symlink loops)
(Note: To colorize the search results, install the "colorize" gem.)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment