In preparation for a client call, I wrote a quick and dirty script to count the kinds of files in a project folder to show them what’s involved. I wanted a nice presentation, and the files grouped into categories. I also added parameters to exclude certain folder patterns from the counter.
The command is
$ ~/Scripts/cfile.rb ~/Projects/Kifu/code/kifu log tmp doc versions
The output is
Project File Counter v0.1 ©hiltmon.com 2012 https://hiltmon.com
Project: kifu
-------------------------------------------------------
Text Files:
Markdown : 1
Plain Text : 1
-------------------------------------------------------
Code Files:
CoffeeScript : 7
CSS : 10
ERB : 251
HTML : 3
JavaScripts : 10
Ruby : 265
SCSS : 10
-------------------------------------------------------
Data Files:
XML : 1
-------------------------------------------------------
Image Files:
GIF Images : 2
Icon Images : 1
PNG Images : 168
-------------------------------------------------------
Configuration Files:
Configuration : 3
Rackup : 1
YML : 4
-------------------------------------------------------
Other Files:
Unknown Kind : 6
.eot files : 2
Locks : 1
Rake : 8
.rdb files : 1
Fonts : 4
-------------------------------------------------------
TOTAL : 760 Files
-------------------------------------------------------
It aint pretty, as it was written very quickly. The script in a gist is:
#! /usr/bin/ruby
# cfile.rb
# Hilton Lipschitz
# Use and modify freely, attribution appreciated
#
# This script counts the number and kinds of files in a project. Any additional
# parameters are treated as exclude folder regexes
#
# Requirements:
#
#
# Examples:
# cfile ~/Folder/DBFS
# cfile ~/Folder/DBFS libs # Will exclude any folders with /libs/
# ~/Scripts/cfile.rb ~/Projects/Kifu/code/kifu log tmp doc versions # To skip all unnecessary rails trees
require 'rubygems'
require 'fileutils'
# No need to report these individually, so merge the counts
@merge_keys = {
".xcbkptlist" => ".xcodeproj",
".xcscheme" => ".xcodeproj",
".xcuserstate" => ".xcodeproj",
".pbxproj" => ".xcodeproj",
".xcworkspacedata" => ".xcodeproj",
".xcsettings" => ".xcodeproj",
".scssc" => ".scss",
".fnt" => ".ttf",
".woff" => ".ttf"
}
# Make nice names for these
@descriptive = {
".xcodeproj" => "Xcode Project",
".c" => "C/Objective-C",
".h" => "C/C++ Headers",
".m" => "Objective-C",
".pch" => "Precompiled Headers",
".plist" => "Plists",
".png" => "PNG Images",
".txt" => "Plain Text",
".strings" => "Strings",
".coffee" => "CoffeeScript",
".conf" => "Configuration",
".css" => "CSS",
".csv" => "CSV",
".erb" => "ERB",
".gif" => "GIF Images",
".god" => "GOD Configs",
".html" => "HTML",
".ico" => "Icon Images",
".js" => "JavaScripts",
".lock" => "Locks",
".log" => "Logs",
".markdown" => "Markdown",
".old" => "OLD",
".pdf" => "PDFs",
".rake" => "Rake",
".rb" => "Ruby",
".scss" => "SCSS",
".ttf" => "Fonts",
".xml" => "XML",
".yml" => "YML",
".ru" => "Rackup",
".xib" => "XIBs",
".storyboard" => "StoryBoards",
}
# Group the ones we know
@categories = {
"Code" => [".c", ".h", ".m", ".coffee", ".erb", ".js", ".rb", ".css", ".scss", ".pch", ".html", ".xib", ".storyboard"],
"Image" => [".gif", ".ico", ".png", ".pdf"],
"Configuration" => [".xcodeproj", ".plist", ".conf", ".yml", ".ru", "strings"],
"Data" => [".xml", ".csv"],
"Text" => [".txt", ".markdown"],
}
@excludes = []
@counter_hash = {}
# Increment the count
def increment_key(key, value = 1)
if @counter_hash[key].nil?
@counter_hash[key] = value
else
@counter_hash[key] = @counter_hash[key] + value
end
end
# Analyze a file by extension
def analyze_file(source)
extension = File.extname(source)
# return if extension.length == 0
increment_key extension
end
def process_folder(source)
@excludes.each do |pattern|
return if source =~ Regexp.new(pattern)
end
source_dir = Dir.new(source)
source_dir.entries.each do |source_file|
# Skip dot files and hidden files
next if source_file =~ /^\./
full_path = File.expand_path(source_file, source_dir.path)
# puts full_path
if File.directory?(full_path)
process_folder full_path
else
analyze_file full_path
end
end
end
def merge_keys
@merge_keys.keys.each do |key|
unless @counter_hash[key].nil?
increment_key @merge_keys[key], @counter_hash[key]
@counter_hash.delete(key)
end
end
end
if ARGV.length == 0
puts "USAGE: cfile ~/Folder/DBFS <excludes>"
exit
end
source = ARGV[0]
if ARGV.length > 1
@excludes = ARGV[1..(ARGV.length)]
# puts @excludes
end
unless File.directory?(source)
puts " ERROR: Parameter 1 must be a project folder..."
exit
end
process_folder source
merge_keys
puts
puts "Project File Counter v0.1 ©hiltmon.com 2012 https://hiltmon.com"
puts
puts "Project: #{File.basename(source)}"
used_keys = []
counter = 0
@categories.keys.each do |category|
array = @categories[category]
header_printed = false
@counter_hash.keys.sort.each do |key|
next if array.index(key).nil?
unless header_printed
puts "-------------------------------------------------------"
puts "#{category} Files:"
header_printed = true
end
used_keys << key if used_keys.index(key).nil?
descriptive = (@descriptive[key] || "#{key} files").ljust(22)
puts " #{descriptive}: #{@counter_hash[key].to_s.rjust(6)}"
counter = counter + @counter_hash[key]
end
end
header_printed = false
@counter_hash.keys.sort.each do |key|
next unless used_keys.index(key).nil?
unless header_printed
puts "-------------------------------------------------------"
puts "Other Files:"
header_printed = true
end
descriptive = (@descriptive[key] || "#{key} files").ljust(22)
descriptive = "Unknown Kind".ljust(22) if key.length == 0
puts " #{descriptive}: #{@counter_hash[key].to_s.rjust(6)}"
counter = counter + @counter_hash[key]
end
puts "-------------------------------------------------------"
puts "TOTAL : #{counter.to_s.rjust(6)} Files"
puts "-------------------------------------------------------"