Skip to content

Instantly share code, notes, and snippets.

@csmoore
Created April 3, 2015 16:06
Show Gist options
  • Save csmoore/15e46dedc5604c784b6d to your computer and use it in GitHub Desktop.
Save csmoore/15e46dedc5604c784b6d to your computer and use it in GitHub Desktop.
Simple find and replace string in files (recursive) python script
#------------------------------------------------------------------------------
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#------------------------------------------------------------------------------
# Filename: FindAndReplace.py
# Purpose: Simple find and replace string in files (recursive) script
# Usage: python FindAndReplace.py [Old String] [New String]
# [File Filters(ex/default:".txt,.html,.erb")] [Directory To Check]
# Requirement: Files must be text (non-binary) files
# (this is why we force you to pick the file pattern/filter)
# WARNING: This will overwrite files matching [File Filters]. All occurrences of [Old String]
# will be replaced with [New String]. Make sure you really, really want to do this.
#------------------------------------------------------------------------------
import os
import sys
import traceback
def usage():
print('Usage: python FindAndReplace.py [Old String] [New String] ' \
'[File Filters(default:".txt,.html,.erb")] [Directory To Check(.)]')
def replaceStringInFile(fileName, oldStringToReplace, newString):
if not(os.path.isfile(fileName) and os.access(fileName, os.W_OK)):
print("WARNING: Skipping...File does not exist or and is not writeable:" + fileName)
return False
fileUpdated = False
# credit/taken/adapted from: http://stackoverflow.com/a/4128194
# Read in old file
with open(fileName, 'r') as f:
newlines = []
for line in f.readlines():
if (oldStringToReplace in line) :
fileUpdated = True
line = line.replace(oldStringToReplace, newString)
newlines.append(line)
# Write changes to same file
if fileUpdated :
print("String Found and Updating File: " + fileName)
try:
with open(fileName, 'w') as f:
for line in newlines:
f.write(line)
except:
print('ERROR: Cannot open/access existing file for writing: ' + fileName)
return fileUpdated
def main():
try:
DEFAULT_PATH = '.'
if len(sys.argv) < 3:
usage()
# old/new string required parameters, exit if not supplied
sys.exit(-1)
else:
oldString = sys.argv[1]
newString = sys.argv[2]
if len(sys.argv) < 4:
patterns = ['.txt', '.html', '.erb']
else:
stringFilter = sys.argv[3]
patterns = stringFilter.split(',')
if len(sys.argv) < 5:
path = DEFAULT_PATH
else:
path = sys.argv[4]
print('[Old String] : ' + oldString)
print('[New String] : ' + newString)
print('[File Filters] : ' + ', '.join(patterns))
print('[Directory To Check] : ' + path)
if not os.path.exists(path):
raise Exception("Selected path does not exist: " + path)
# Walks through directory structure looking for files matching patterns
matchingFileList = \
[os.path.join(dp, f) \
for dp, dn, filenames in os.walk(path) \
for f in filenames \
if os.path.splitext(f)[1] in patterns]
print('Files found matching patterns: ' + str(len(matchingFileList)))
fileCount = 0
filesReplaced = 0
for currentFile in matchingFileList:
fileCount+=1
fileReplaced = replaceStringInFile(currentFile, oldString, newString)
if fileReplaced:
filesReplaced+=1
print("Total Files Searched : " + str(fileCount))
print("Total Files Replaced/Updated : " + str(filesReplaced))
except Exception as err:
print(traceback.format_exception_only(type(err), err)[0].rstrip())
sys.exit(-1)
if __name__ == '__main__':
main()
@csmoore
Copy link
Author

csmoore commented Oct 22, 2019

For a large file - I would probably restructure this script to read/write one line at a time.

@DaveHCL
Copy link

DaveHCL commented Oct 22, 2019

So I was able to get the script to work. I have to add encoding for the read and write phases. And I went from 1174 ocurances of the OldString down to 11. And those are embedded into some xml tags. For which I just did a find/replace in Notepad++ as I wanted to get this script to my teammate.
Thanks!!

@csmoore
Copy link
Author

csmoore commented Oct 23, 2019

Glad you were able to get something working - I guess Notepad++ is indeed another option if it is a non-batch case - but then you never get the fun of learning to work with unicode

@DaveHCL
Copy link

DaveHCL commented Oct 23, 2019

Ok so now you are being evil :) Putting a link to what seems to be a well crafted article on Unicode, gets me reading about it. And wanting to then use it. Almost like a real, grown up programmer. Which I am not by trade but am getting into more and more.
Thanks for the link. And for this script!

@Priya12233
Copy link

Hi
I tried to use your program to work with .xml file, and I am getting error. If possible plz check the below stackoverflow link for my query

https://stackoverflow.com/questions/67431852/why-the-python-file-handling-not-recognizing-file-name?noredirect=1#comment119189069_67431852

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment