Hiltmon

On walkabout in life and technology

Kinds of Files in a Project Folder

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

1
$ ~/Scripts/cfile.rb ~/Projects/Kifu/code/kifu log tmp doc versions

The output is

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
Project File Counter v0.1 ©hiltmon.com 2012 http://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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
#! /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 http://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 "-------------------------------------------------------"

Comments