· 8 years ago · Apr 09, 2018, 05:56 AM
1#!/usr/bin/env python
2# -*- coding:utf-8 -*-
3# Script generates statistics table for specified direcories sorted by type(by
4# default), overall files size or files count. This script can also sort files
5# into the directories by type using hardlinks(default) or softlinks. Type
6# determination is done by `file` utility.
7#
8# To get help just run script with wrong parameters.
9
10import os
11import sys
12import subprocess
13import string
14import getopt
15import operator
16
17def usage():
18 print "Usage: fmetric.py [-C|-S] [-h] [-m] [-b [-s]] [-q] DIR [DIR] ...\n\
19 \t-C\tSort by files count\n\
20 \t-S\tSort by overall size\n\
21 \t-h\tShow sizes in human-readable format\n\
22 \t-m\tUse MIME types instead of file descriptions\n\
23 \t-b\tCreate backup directory\n\
24 \t-s\tUse softlinks (only useful with -b option)\n\
25 \t-q\tUse \"quiet\" mode (do not write any error messages, skip on errors)"
26
27
28def file_info(filename, flags):
29 file_binary = "/usr/bin/file"
30 (file_stdout, file_stderr) = subprocess.Popen( \
31 [file_binary, flags, filename], \
32 stdout=subprocess.PIPE,\
33 stderr=subprocess.PIPE).communicate()
34 return file_stdout.split(",")[0].strip('\n')
35
36
37def sizeof_fmt(num, readable):
38 if not readable:
39 return num
40
41 for x in ['bytes','KB','MB','GB','TB']:
42 if num < 1024.0:
43 return "%3.1f %s" % (num, x)
44 num /= 1024.0
45
46
47def main(argv):
48 # Initializing basic variables
49 backup = False
50 readable = False
51 use_symlinks = False
52 quiet_mode = False
53 info_flags = "-b"
54 sort_key = 0
55 info_list = []
56 size_list = []
57 count_list = []
58
59 # Parsing command-line arguments
60 try:
61 opts, args = getopt.getopt(argv, "CShmbsq")
62 except getopt.GetoptError:
63 usage()
64 sys.exit(2)
65
66 for opt, arg in opts:
67 if opt == "-h":
68 readable = True
69
70 if (opt == "-S"):
71 if (sort_key > 0):
72 usage()
73 sys.exit(2)
74
75 sort_key = 1
76
77 if (opt == "-C"):
78 if (sort_key > 0):
79 usage()
80 sys.exit(2)
81
82 sort_key = 2
83
84 if (opt == "-b"):
85 backup = True
86
87 if (opt == "-m"):
88 info_flags = "-bi"
89
90 if (opt == "-s"):
91 use_symlinks = True
92
93 if (opt == "-q"):
94 quiet_mode = True
95
96 # Walk through the files and gather stats
97 for dir in args:
98 backup_dir = os.path.join(dir, "backup")
99 if backup and os.path.exists(backup_dir):
100 print "Backup directory \"%s\" exists, exiting" % backup_dir
101 sys.exit(1)
102
103 for root, dirs, files in os.walk(dir):
104 for name in files:
105 filename = os.path.join(root, name)
106
107 # This might cause some errors for moved files, broken links,
108 # etc. Output all errors to stderr, do not stop the script
109 try:
110 size = os.path.getsize(filename)
111 except OSError, (errno, strerror):
112 if not quiet_mode:
113 sys.stderr.write( "Error reading file size for %s: %s\n" % \
114 (filename, strerror) )
115 continue
116
117 info = file_info(filename, info_flags)
118
119 # Add stats to the comparison table
120 if info in info_list:
121 index = info_list.index(info)
122 size_list[index] += size
123 count_list[index] += 1
124 else:
125 info_list.append(info)
126 size_list.append(size)
127 count_list.append(1)
128
129 # Create backup hardlinks/symlinks
130 if backup:
131 backup_path = os.path.join(backup_dir, info)
132 link_path = os.path.join(backup_path, name)
133 if not os.path.exists(backup_path):
134 os.makedirs(backup_path)
135
136 try:
137 if use_symlinks:
138 os.symlink(filename, link_path)
139 else:
140 os.link(filename, link_path)
141
142 except OSError, (errno, strerror):
143 if not quiet_mode:
144 sys.stderr.write( "Error creating link %s: %s\n" % \
145 (link_path, strerror) )
146
147
148 info_field_size = len(max(info_list, key=len))
149
150 # Print header
151 print "%s | %s | %s" % ( string.ljust("Type",info_field_size), \
152 string.ljust("Size", 12), \
153 "Count")
154 print string.ljust("", info_field_size + 3 + 12 + 3 + 12, "=")
155
156
157 # Print the result
158 for info, size, count in sorted(zip(info_list, size_list, count_list), \
159 key = operator.itemgetter(sort_key), reverse = (sort_key <> 0)):
160 print "%s | %s | %d" % (string.ljust(info, info_field_size), \
161 string.ljust(str(sizeof_fmt(size, readable)), 12), \
162 count )
163
164
165if __name__ == '__main__':
166 if len(sys.argv) < 2:
167 usage()
168 sys.exit(2)
169
170 main(sys.argv[1:])