#!/usr/local/cpython-3.7/bin/python3 """Manage timestamps files, to see if a directory's contents have changed.""" import os import sys import typing import filecmp import subprocess TIMESTAMP_FILENAME = '.file-timestamps' TEMP_TIMESTAMP_FILENAME = TIMESTAMP_FILENAME + '.temp' def usage(retval: int) -> None: """Output a usage message.""" if retval == 0: write = sys.stdout.write else: write = sys.stderr.write # write('Usage: {} --generate dir --compare dir1 dir2 --help'.format(sys.argv[0])) write('Usage: {} --help --verbose --generate dir1 dir2 ... dirn\n'.format(sys.argv[0])) sys.exit(retval) def run(command: str) -> None: """Run shell command.""" retval = subprocess.call(command, shell=True) if retval: sys.stderr.write('Command {} failed with exit code {}\n'.format(command, retval)) sys.exit(1) def generate(directory: str) -> bool: """Generate a fts file for directory.""" filenames = [filename for filename in os.listdir(directory) if not filename.startswith('.')] # filenames = [] # for filename in os.listdir(directory): # if filename.startswith('.'): # continue # filenames.append(filename) # I doubt filenames are guaranteed to come back sorted from os.listdir(), so we sort them ourselves. filenames.sort() temp_ts_fn = os.path.join(directory, TEMP_TIMESTAMP_FILENAME) ts_fn = os.path.join(directory, TIMESTAMP_FILENAME) with open(temp_ts_fn, 'w') as file_: for filename in filenames: fn = os.path.join(directory, filename) # We intentionally use a naked filename instead of fn in the 3rd field of 3. file_.write('{} {} {}\n'.format(os.path.getmtime(fn), os.path.getsize(fn), filename)) # The "original" file does not exist - rename, output equal, and exit 0 if not os.path.exists(ts_fn): os.rename(temp_ts_fn, ts_fn) return True # We exit 0 whether things look the same or not, because an error would look like exit 1, and # we want to be able to tell the difference. if filecmp.cmp(ts_fn, temp_ts_fn, shallow=False): os.unlink(temp_ts_fn) return True else: os.rename(temp_ts_fn, ts_fn) return False # def inhale(filename: str) -> None: # """Return filename's mtime and a list of lines within it.""" # dir_stamps = [] # with open(filename, 'r') as file_: # for line in file_.readline(): # dir_stamps.append(line) # return (os.path.getmtime(filename), dir_stamps) def main() -> None: """Manage timestamps files, to see if a directory's contains have changed.""" directories: typing.List[str] = [] verbose = False while sys.argv[1:]: if sys.argv[1] == '--generate': directories = sys.argv[2:] del sys.argv[1] break elif sys.argv[1] == '--verbose': verbose = True # elif sys.argv[1] == '--compare': # directory1 = sys.argv[2] # directory2 = sys.argv[3] # del sys.argv[1] # del sys.argv[1] # compare(initial_cwd, directory1, directory2) elif sys.argv[1] in ('-h', '--help'): usage(0) else: sys.stderr.write('{}: Unrecognized option: {}\n'.format(sys.argv[0], sys.argv[1])) usage(1) del sys.argv[1] if not directories: sys.stderr.write('{}: --generate is a required option\n'.format(sys.argv[0])) usage(1) all_equal = True for directory in directories: if verbose: sys.stderr.write('Generating timestamp file for {}\n'.format(directory)) subresult = generate(directory) all_equal &= subresult if subresult: print('equal') else: print('unequal') if all_equal: if verbose: sys.stderr.write('All equal\n') else: sys.stderr.write('One or more not equal\n') sys.exit(0) if __name__ == '__main__': main()