<div dir="ltr">greetings,<div><br></div><div>i'm a long time perl programmer who is learning python.  i'd be interested in any comments you might have on my code below.  feel free to respond privately if you prefer.  i'd like to know if i'm on the right track.  the program works, and does what i want it to do.  is there a different way a seasoned python programmer would have done things?  i would like to learn the culture as well as the language.  am i missing anything?  i know i'm not doing error checking below.  i suppose comments would help, too.</div><div><br></div><div>i wanted a program to scan a tree and for each regular file, print a line of text to stdout with information about the file.  this will be data for another program i want to write which finds sets of duplicate files larger than a parameter size.  that is, using output from this program, the sets of files i want to find are on the same filesystem on the same host (obviously, but i include hostname in the data to be sure), and must have the same md5 sum, but different inode numbers.</div><div><br></div><div>the output of the code below is easier for a human to read when paged through 'less', which on my mac renders the ascii nuls as "^@" in reverse video.<br></div><div><br></div><div>thanks,</div><div>david</div><div><br></div><div><br></div><div><div><font face="courier new, monospace"><b>usage: dupscan [-h] [--start-directory START_DIRECTORY]</b></font></div><div><font face="courier new, monospace"><b><br></b></font></div><div><font face="courier new, monospace"><b>scan files in a tree and print a line of information about each regular file</b></font></div><div><font face="courier new, monospace"><b><br></b></font></div><div><font face="courier new, monospace"><b>optional arguments:</b></font></div><div><font face="courier new, monospace"><b>  -h, --help            show this help message and exit</b></font></div><div><font face="courier new, monospace"><b>  --start-directory START_DIRECTORY, -d START_DIRECTORY</b></font></div><div><font face="courier new, monospace"><b>                        specifies the root of the filesystem tree to be</b></font></div><div><font face="courier new, monospace"><b>                        processed</b></font></div></div><div><br></div><div><br></div><div><br></div><div><br></div><div><div><div><font face="courier new, monospace" size="1"><b>#!/usr/bin/python</b></font></div><div><font face="courier new, monospace" size="1"><b><br></b></font></div><div><font face="courier new, monospace" size="1"><b>import argparse</b></font></div><div><font face="courier new, monospace" size="1"><b>import hashlib</b></font></div><div><font face="courier new, monospace" size="1"><b>import os</b></font></div><div><font face="courier new, monospace" size="1"><b>import re</b></font></div><div><font face="courier new, monospace" size="1"><b>import socket</b></font></div><div><font face="courier new, monospace" size="1"><b>import sys</b></font></div><div><font face="courier new, monospace" size="1"><b><br></b></font></div><div><font face="courier new, monospace" size="1"><b>from stat import *</b></font></div><div><font face="courier new, monospace" size="1"><b><br></b></font></div><div><font face="courier new, monospace" size="1"><b>ascii_nul = chr(0)</b></font></div><div><font face="courier new, monospace" size="1"><b><br></b></font></div><div><font face="courier new, monospace" size="1"><b>     # from: <a href="http://stackoverflow.com/questions/1131220/get-md5-hash-of-big-files-in-python">http://stackoverflow.com/questions/1131220/get-md5-hash-of-big-files-in-python</a></b></font></div><div><font face="courier new, monospace" size="1"><b>     # except that i use hexdigest() rather than digest()</b></font></div><div><font face="courier new, monospace" size="1"><b>def md5_for_file(f, block_size=2**20):</b></font></div><div><font face="courier new, monospace" size="1"><b>  md5 = hashlib.md5()</b></font></div><div><font face="courier new, monospace" size="1"><b>  while True:</b></font></div><div><font face="courier new, monospace" size="1"><b>    data = f.read(block_size)</b></font></div><div><font face="courier new, monospace" size="1"><b>    if not data:</b></font></div><div><font face="courier new, monospace" size="1"><b>      break</b></font></div><div><font face="courier new, monospace" size="1"><b>    md5.update(data)</b></font></div><div><font face="courier new, monospace" size="1"><b>  return md5.hexdigest()</b></font></div><div><font face="courier new, monospace" size="1"><b><br></b></font></div><div><font face="courier new, monospace" size="1"><b>thishost = socket.gethostname()</b></font></div><div><font face="courier new, monospace" size="1"><b><br></b></font></div><div><font face="courier new, monospace" size="1"><b>parser = argparse.ArgumentParser(description='scan files in a tree and print a line of information about each regular file')</b></font></div><div><font face="courier new, monospace" size="1"><b>parser.add_argument('--start-directory', '-d', default='.', help='specifies the root of the filesystem tree to be processed')</b></font></div><div><font face="courier new, monospace" size="1"><b>args = parser.parse_args()</b></font></div><div><font face="courier new, monospace" size="1"><b><br></b></font></div><div><font face="courier new, monospace" size="1"><b>start_directory = re.sub( '/+$', '', args.start_directory )</b></font></div><div><font face="courier new, monospace" size="1"><b><br></b></font></div><div><font face="courier new, monospace" size="1"><b>for directory_path, directory_names, file_names in os.walk( start_directory ):</b></font></div><div><font face="courier new, monospace" size="1"><b>  for file_name in file_names:</b></font></div><div><font face="courier new, monospace" size="1"><b>    file_path = "%s/%s" % ( directory_path, file_name )</b></font></div><div><font face="courier new, monospace" size="1"><b><br></b></font></div><div><font face="courier new, monospace" size="1"><b>    lstat_info = os.lstat( file_path )</b></font></div><div><font face="courier new, monospace" size="1"><b><br></b></font></div><div><font face="courier new, monospace" size="1"><b>    mode = lstat_info.st_mode</b></font></div><div><font face="courier new, monospace" size="1"><b><br></b></font></div><div><font face="courier new, monospace" size="1"><b>    if not S_ISREG( mode ) or S_ISLNK( mode ):</b></font></div><div><font face="courier new, monospace" size="1"><b>      continue</b></font></div><div><font face="courier new, monospace" size="1"><b><br></b></font></div><div><font face="courier new, monospace" size="1"><b>    f = open( file_path, 'r' )</b></font></div><div><font face="courier new, monospace" size="1"><b>    md5sum = md5_for_file( f )</b></font></div><div><font face="courier new, monospace" size="1"><b><br></b></font></div><div><font face="courier new, monospace" size="1"><b>    dev   = lstat_info.st_dev</b></font></div><div><font face="courier new, monospace" size="1"><b>    ino   = lstat_info.st_ino</b></font></div><div><font face="courier new, monospace" size="1"><b>    nlink = lstat_info.st_nlink</b></font></div><div><font face="courier new, monospace" size="1"><b>    size  = lstat_info.st_size</b></font></div><div><font face="courier new, monospace" size="1"><b><br></b></font></div><div><font face="courier new, monospace" size="1"><b>    sep = ascii_nul</b></font></div><div><font face="courier new, monospace" size="1"><b><br></b></font></div><div><font face="courier new, monospace" size="1"><b>    print "%s%c%s%c%d%c%d%c%d%c%d%c%s" % ( thishost, sep, md5sum, sep, dev, sep, ino, sep, nlink, sep, size, sep, file_path )</b></font></div><div><font face="courier new, monospace" size="1"><b><br></b></font></div><div><font face="courier new, monospace" size="1"><b>exit( 0 )</b></font></div></div><div><br></div><div><br></div><div><br></div>-- <br><div dir="ltr">Our decisions are the most important things in our lives.<div>***</div><div>Live in a world of your own, but always welcome visitors.</div></div>
</div></div>