mirror of
https://github.com/FAUSheppy/config
synced 2026-06-18 22:32:36 +02:00
fix: add simple search and replace script
This commit is contained in:
Executable
+130
@@ -0,0 +1,130 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import argparse
|
||||
from pathlib import Path
|
||||
import sys
|
||||
|
||||
|
||||
def find_files(directory: Path, recursive: bool):
|
||||
if recursive:
|
||||
yield from (p for p in directory.rglob("*") if p.is_file())
|
||||
else:
|
||||
yield from (p for p in directory.iterdir() if p.is_file())
|
||||
|
||||
def process_file(file_path: Path, search: str, replace: str):
|
||||
try:
|
||||
content = file_path.read_text(encoding="utf-8")
|
||||
except (UnicodeDecodeError, OSError):
|
||||
return None
|
||||
|
||||
if search not in content:
|
||||
return None
|
||||
|
||||
changed_lines = []
|
||||
|
||||
for lineno, line in enumerate(content.splitlines(), start=1):
|
||||
if search in line:
|
||||
changed_lines.append({
|
||||
"line": lineno,
|
||||
"old": line,
|
||||
"new": line.replace(search, replace),
|
||||
})
|
||||
|
||||
return {
|
||||
"path": file_path,
|
||||
"count": content.count(search),
|
||||
"new_content": content.replace(search, replace),
|
||||
"changed_lines": changed_lines,
|
||||
}
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Search and replace text in files with confirmation."
|
||||
)
|
||||
parser.add_argument(
|
||||
"directory",
|
||||
help="Directory to search"
|
||||
)
|
||||
parser.add_argument(
|
||||
"search",
|
||||
help="String to search for"
|
||||
)
|
||||
parser.add_argument(
|
||||
"replace",
|
||||
help="Replacement string"
|
||||
)
|
||||
parser.add_argument(
|
||||
"-r",
|
||||
"--recursive",
|
||||
action="store_true",
|
||||
help="Search recursively"
|
||||
)
|
||||
parser.add_argument(
|
||||
"-y",
|
||||
"--yes",
|
||||
action="store_true",
|
||||
help="Skip confirmation"
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
directory = Path(args.directory)
|
||||
|
||||
if not directory.is_dir():
|
||||
print(f"Error: '{directory}' is not a directory.", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
changes = []
|
||||
|
||||
for file_path in find_files(directory, args.recursive):
|
||||
result = process_file(file_path, args.search, args.replace)
|
||||
if result:
|
||||
changes.append(result)
|
||||
|
||||
if not changes:
|
||||
print("No matches found.")
|
||||
return
|
||||
|
||||
print("\nPlanned changes:\n")
|
||||
total_replacements = 0
|
||||
|
||||
for change in changes:
|
||||
print("=" * 80)
|
||||
print(f"File: {change['path']}")
|
||||
print(f"Replacements: {change['count']}")
|
||||
print()
|
||||
|
||||
for line_change in change["changed_lines"]:
|
||||
print(f"Line {line_change['line']}:")
|
||||
print(f" - {line_change['old']}")
|
||||
print(f" + {line_change['new']}")
|
||||
print()
|
||||
|
||||
total_replacements += change["count"]
|
||||
|
||||
if not args.yes:
|
||||
try:
|
||||
response = input("\nApply these changes? [y/N]: ").strip().lower()
|
||||
except KeyboardInterrupt:
|
||||
print("Aborted")
|
||||
sys.exit(1)
|
||||
|
||||
if response.lower() not in ("y", "yes"):
|
||||
print("Aborted.")
|
||||
return
|
||||
|
||||
for change in changes:
|
||||
change["path"].write_text(
|
||||
change["new_content"],
|
||||
encoding="utf-8"
|
||||
)
|
||||
|
||||
print(
|
||||
f"Completed. Modified {len(changes)} file(s), "
|
||||
f"{total_replacements} replacement(s)."
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user