Fwd: program to generate data helpful in finding duplicate large files

David Alban extasia at extasia.org
Fri Sep 19 09:32:02 EDT 2014


here is my reworked code in a plain text email.

---------- Forwarded message ----------
From: <bizcor at gmail.com>
Date: Thu, Sep 18, 2014 at 3:58 PM
Subject: Re: program to generate data helpful in finding duplicate large
files
To: python-list at python.org


thanks for the responses.   i'm having quite a good time learning python.

On Thu, Sep 18, 2014 at 11:45 AM, Chris Kaynor <ckaynor at zindagigames.com>
wrote:
>
> Additionally, you may want to specify binary mode by using
open(file_path, 'rb') to ensure platform-independence ('r' uses Universal
newlines, which means on Windows, Python will convert "\r\n" to "\n" while
reading the file). Additionally, some platforms will treat binary files
differently.


would it be good to use 'rb' all the time?

On Thu, Sep 18, 2014 at 11:48 AM, Chris Angelico <rosuav at gmail.com> wrote:
>
> On Fri, Sep 19, 2014 at 4:11 AM, David Alban <extasia at extasia.org> wrote:
> > exit( 0 )
>
> Unnecessary - if you omit this, you'll exit 0 implicitly at the end of
> the script.


aha.  i've been doing this for years even with perl, and apparently it's
not necessary in perl either.  i was influenced by shell.

this shell code:

     if [[ -n $report_mode ]] ; then
        do_report
     fi

     exit 0

is an example of why you want the last normally executed shell statement to
be "exit 0".  if you omit the exit statement it in this example, and
$report_mode is not set, your shell program will give a non-zero return
code and appear to have terminated with an error.  in shell the last
expression evaluated determines the return code to the os.

ok, i don't need to do this in python.

On Thu, Sep 18, 2014 at 1:23 PM, Peter Otten <__peter__ at web.de> wrote:
>
> file_path may contain newlines, therefore you should probably use "\0" to
> separate the records.


i chose to stick with ascii nul as the default field separator, but i added
a --field-separator option in case someone wants human readable output.

style question:  if there is only one, possibly short statement in a block,
do folks usually move it up to the line starting the block?

  if not S_ISREG( mode ) or S_ISLNK( mode ):
    return

vs.

  if not S_ISREG( mode ) or S_ISLNK( mode ): return

or even:

  with open( file_path, 'rb' ) as f: md5sum = md5_for_file( file_path )



fyi, here are my changes:


usage: dupscan [-h] [--start-directory START_DIRECTORY]
               [--field-separator FIELD_SEPARATOR]

scan files in a tree and print a line of information about each regular file

optional arguments:
  -h, --help            show this help message and exit
  --start-directory START_DIRECTORY, -d START_DIRECTORY
                        Specify the root of the filesystem tree to be
                        processed. The default is '.'
  --field-separator FIELD_SEPARATOR, -s FIELD_SEPARATOR
                        Specify the string to use as a field separator in
                        output. The default is the ascii nul character.



#!/usr/bin/python

import argparse
import hashlib
import os

from platform import node
from stat import S_ISREG, S_ISLNK

ASCII_NUL = chr(0)

     # from:
http://stackoverflow.com/questions/1131220/get-md5-hash-of-big-files-in-python
     # except that i use hexdigest() rather than digest()
def md5_for_file( path, block_size=2**20 ):
  md5 = hashlib.md5()
  with open( path, 'rb' ) as f:
    while True:
      data = f.read(block_size)
      if not data:
        break
      md5.update(data)
  return md5.hexdigest()

def file_info( directory, basename, field_separator=ASCII_NUL ):
  file_path = os.path.join( directory, basename )
  st = os.lstat( file_path )

  mode = st.st_mode
  if not S_ISREG( mode ) or S_ISLNK( mode ):
    return

  with open( file_path, 'rb' ) as f:
    md5sum = md5_for_file( file_path )

  return field_separator.join( [ thishost, md5sum, str( st.st_dev ), str(
st.st_ino ), str( st.st_nlink ), str( st.st_size ), file_path ] )

if __name__ == "__main__":
  parser = argparse.ArgumentParser(description='scan files in a tree and
print a line of information about each regular file')
  parser.add_argument('--start-directory', '-d', default='.',
help='''Specify the root of the filesystem tree to be processed.  The
default is '.' ''')
  parser.add_argument('--field-separator', '-s', default=ASCII_NUL,
help='Specify the string to use as a field separator in output.  The
default is the ascii nul character.')
  args = parser.parse_args()

  start_directory = args.start_directory.rstrip('/')
  field_separator = args.field_separator

  thishost = node()
  if thishost == '':
    thishost='[UNKNOWN]'

  for directory_path, directory_names, file_names in os.walk(
start_directory ):
    for file_name in file_names:
      print file_info( directory_path, file_name, field_separator )

-- 
Our decisions are the most important things in our lives.
***
Live in a world of your own, but always welcome visitors.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/python-list/attachments/20140919/2f52429c/attachment.html>


More information about the Python-list mailing list