#### Source code for ./examples/find_and_replace.py ####
#!/usr/bin/env python
"""my first sysadmin module
added a find option function
added config exclude_dirs with ConfigParser
added edit file function
added backup function
test find and replace
"""
# *****************************************************************************
from __future__ import generators
import sys
import os
import stat
import re
from ConfigParser import ConfigParser
import tarfile
# *****************************************************************************
# read config file defaults
RCFILE = os.path.join(os.getenv('HOME'), 'pyadmin.rc')
PARSER = ConfigParser()
PARSER.read([RCFILE])
EXCLUDE_DIRS = PARSER.get('walk', 'exclude')
BACKUP_DIR = PARSER.get('backups', 'budir')
BACKUP_FILE = PARSER.get('backups', 'bufile')
# -----------------------------------------------------------------------------
def walktree(top='.', depthfirst=True, ):
"""Directory tree generator.
Traverses filesystem from directory 'top' downwards, returning each
directory found and a list of files in that directory.
If depthfirst is True, returns files found from bottom of tree first.
See also os.walk, available in Python 2.3 on.
Thanks to Noah Spurrier and Doug Fort.
"""
names = os.listdir(top)
if not depthfirst:
yield top, names
for name in names:
try:
state = os.lstat(os.path.join(top, name))
except os.error:
continue
if stat.S_ISDIR(state.st_mode)and (name not in EXCLUDE_DIRS):
for (newtop, children)in \
walktree(os.path.join(top, name), depthfirst):
yield newtop, children
if depthfirst:
yield top, names
# -----------------------------------------------------------------------------
def find(paths=None, inc_pattern='.*',
exc_pattern='', newer_than=None, prt=False, ):
"""Find files below path(s) matching inc_pattern.
exc_pattern = pattern to exclude files.
paths = list of paths to search.
Returns a list of files found.
If prt is True, prints full pathname of files found on stdout.
If newer_than is set to the name of a file, only files newer than that
file will be returned.
"""
wanted = True
if newer_than:
# get ctime of stamp file
try:
stamp_date = os.lstat(newer_than)[stat.ST_CTIME]
except os.error:
raise
results = []
inc_rgx = re.compile(inc_pattern)
exc_rgx = None
if exc_pattern:
exc_rgx = re.compile(exc_pattern)
if not paths:
paths = ['.', ]
if isinstance(paths, str):
paths = [paths, ]
for path in paths:
for basepath, children in walktree(path, False, ):
for child in children:
if inc_rgx.match(child, ):
if not exc_rgx or not exc_rgx.match(child):
fullpath = os.path.join(basepath, child, )
if newer_than:
# see if file mod time is later than stamp file
state = os.lstat(fullpath)
wanted = (state[stat.ST_CTIME]>stamp_date)
if wanted:
if prt:
sys.stdout.write('%s\n'%fullpath)
results.append(fullpath)
return results
# -----------------------------------------------------------------------------
def edit_files(file_list, old_string, new_string, update=False, ):
"""replace all occurrences of old_string in files/file_list by new_string
Returns list of files which were changed.
If update is False, the files will not be updated (useful to see what
files would have been changed).
"""
changed_list = []
for fname in file_list:
try:
fobj = open(fname, )
except IOError:
sys.stderr.write('Error opening file %s\n'%fname)
raise
lines = fobj.readlines()
fobj.close()
newlines = []
for line in lines:
line = line.replace(old_string, new_string, )
newlines.append(line)
if newlines != lines:
# we have changed the text, write it out
if update:
fobj = open(fname, 'w', )
fobj.writelines(newlines)
fobj.close()
changed_list.append(fname)
return changed_list
# -----------------------------------------------------------------------------
def backup(file_list, backup_file=None, verbose=False, ):
"""make tar backup of files listed in file_list to backup_file"""
if not file_list:
return
if not backup_file:
backup_file = os.sep.join([BACKUP_DIR, BACKUP_FILE, ])
tgz = tarfile.open(backup_file, 'w:gz', )
for file_name in file_list:
if file_name.find(os.sep) == 0:
# store file name as relative path in archive
rel_name = file_name[1: ]
else:
rel_name = None
tgz.add(name = file_name, arcname = rel_name, recursive = False, )
if verbose:
sys.stdout.write('%s\n'%file_name)
tgz.close()
return
# *****************************************************************************
if __name__ == '__main__':
sys.stdout.write('Enter directory paths to find in >> ')
PATHS = sys.stdin.readline().strip()
if not PATHS:
sys.exit()
PATHS = PATHS.split()
sys.stdout.write('Pattern to match >> ')
INCLUDE = sys.stdin.readline().strip()
if not INCLUDE:
INCLUDE = '.*'
sys.stdout.write('Pattern to exclude >> ')
EXCLUDE = sys.stdin.readline().strip()
FILE_LIST = find(PATHS, inc_pattern = INCLUDE, exc_pattern = EXCLUDE,
newer_than = None, prt = True)
if not FILE_LIST:
sys.stdout.write('no matching files\n')
sys.stdout.write('%s matching files found\n'%len(FILE_LIST))
sys.stdout.write('String to find >> ')
OLD = sys.stdin.readline().strip()
if not OLD:
sys.exit()
sys.stdout.write('String to replace >> ')
NEW = sys.stdin.readline().strip()
if not NEW:
sys.exit()
CHANGED_FILES = edit_files(FILE_LIST, OLD, NEW, update = False, )
if not CHANGED_FILES:
sys.stdout.write('no matching strings\n')
sys.exit()
sys.stdout.write('%s files with matching strings found\n'% \
len(CHANGED_FILES))
sys.stdout.write('OK to edit files ? [y|n] [n] >> ')
OK = sys.stdin.readline().upper().strip()
if OK != 'Y':
sys.exit()
sys.stdout.write('Taking backup...\n')
backup(CHANGED_FILES)
sys.stdout.write('Backup completed, carrying out edit...\n')
CHANGED_FILES = edit_files(FILE_LIST, OLD, NEW, update = True, )
# *****************************************************************************
[Created with py2html Ver:0.62]