Skip to content

Instantly share code, notes, and snippets.

@brayevalerien
Created September 5, 2025 13:47
Show Gist options
  • Select an option

  • Save brayevalerien/4d6c195d743c0ff999c866342cf1d89b to your computer and use it in GitHub Desktop.

Select an option

Save brayevalerien/4d6c195d743c0ff999c866342cf1d89b to your computer and use it in GitHub Desktop.
Search and replace text in all text files within a directory (recursively). Usage: python search_replace.py <directory> <search_text> <replace_text>
#!/usr/bin/env python3
"""
search_replace.py - Search and replace text in all text files within a directory (recursively)
Usage:
python search_replace.py <directory> <search_text> <replace_text>
Example:
python search_replace.py "C:\Documents\Project" "TODO" "DONE"
python search_replace.py . "old_function" "new_function"
Features:
- Recursively processes all subdirectories
- UTF-8 encoding support
- Error handling for inaccessible files
- Reports modified files and total count
- Preserves file permissions and timestamps
Requirements:
- Python 3.6+
- Windows/Linux/MacOS compatible
Author: Valérien (brayevalerien)
License: MIT
"""
import os
import sys
from pathlib import Path
TEXT_EXTENSIONS = {'.txt', '.md', '.log', '.csv', '.json', '.xml', '.yaml', '.yml',
'.ini', '.cfg', '.conf', '.py', '.js', '.html', '.css', '.sql'}
def search_replace(directory, search_text, replace_text, extensions=TEXT_EXTENSIONS):
"""
Search and replace text in all matching files within a directory.
Args:
directory (str): Path to target directory
search_text (str): Text to search for
replace_text (str): Text to replace with
extensions (set): Set of file extensions to process
Returns:
int: Number of files modified
"""
directory = Path(directory)
if not directory.exists():
print(f"Error: Directory '{directory}' does not exist")
return 0
if not directory.is_dir():
print(f"Error: '{directory}' is not a directory")
return 0
modified_count = 0
error_count = 0
# Walk through directory tree
for filepath in directory.rglob('*'):
if filepath.is_file() and filepath.suffix.lower() in extensions:
try:
with open(filepath, 'r', encoding='utf-8') as f:
content = f.read()
if search_text in content:
# Replace and write back
new_content = content.replace(search_text, replace_text)
with open(filepath, 'w', encoding='utf-8') as f:
f.write(new_content)
modified_count += 1
print(f"Modified: {filepath}")
except PermissionError:
error_count += 1
print(f"Permission denied: {filepath}")
except UnicodeDecodeError:
error_count += 1
print(f"Encoding error: {filepath}")
except Exception as e:
error_count += 1
print(f"Error processing {filepath}: {e}")
# Summary
print(f"\n{'_'*50}")
print(f"Summary:")
print(f" - Files modified: {modified_count}")
if error_count > 0:
print(f" - Files with errors: {error_count}")
print(f"{'_'*50}")
return modified_count
def main():
"""Main entry point for command-line usage."""
if len(sys.argv) != 4:
print("Usage: python search_replace.py <directory> <search_text> <replace_text>")
print("\nExamples:")
print(' python search_replace.py "C:\\Projects" "localhost" "127.0.0.1"')
print(' python search_replace.py . "print(" "logging.debug("')
sys.exit(1)
directory = sys.argv[1]
search_text = sys.argv[2]
replace_text = sys.argv[3]
print(f"Search and Replace Operation")
print(f"{'_'*50}")
print(f"Directory: {os.path.abspath(directory)}")
print(f"Search for: '{search_text}'")
print(f"Replace with: '{replace_text}'")
print(f"File types: {', '.join(sorted(TEXT_EXTENSIONS))}")
print(f"{'_'*50}\n")
# Perform search and replace
modified = search_replace(directory, search_text, replace_text)
# Exit with appropriate code
sys.exit(0 if modified > 0 else 1)
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment