這幾天接手到了一個很老很老的項目,PHP的,裏面的數據庫地址不是同一配置的。有很多子站點,每個字站點又有自己的配置文件,這個時候,問題來了,要換數據庫地址了!!!
初聞這個問題,我整個人都是懵逼的,這不是搞我嘛?這麼多改起來得多麻煩?
但活兒還是得做啊,於是作爲一名機智的大彩筆,我決定寫個Python腳本解決這個問題。
當然,這個問題可以被簡單理解爲,給定路徑a,字符串列表b,字符串c,請將路徑a下的全部文本文件(包括若干級子目錄下的文本文件)中的字符串列表b中的全部字符串統一替換爲字符串c。
腳本編寫如下:
# -*- coding: utf-8 -*-
# @File : replace_content.py
# @Author: AaronJny
# @Date : 2019/11/21
# @Desc : 給定路徑a,字符串列表b,字符串c,請將路徑a下的全部文本文件(包括若干級子目錄下的文本文件)中的字符串列表b中的全部字符串統一替換爲字符串c。
import logging
import os
from pprint import pprint
import time
# 設置日誌輸出格式
logging.basicConfig(format='%(asctime)s - %(pathname)s[line:%(lineno)d] - %(levelname)s: %(message)s',
level=logging.INFO)
# 需要進行替換的文件的根路徑
ROOT_PATH = '/home/aaron/code/'
# 待替換的原始字符串
ORIGIN_STRING_LIST = ['張三', '李四']
# 用於替換的目標字符串
TARGET_STRING = '王五'
# 需要替換的文件的擴展名列表
FILENAME_EXTENSIONS = ['.php']
# 文件編碼類型
ENCODE_TYPE = 'utf-8'
check_results = []
def check_filename_extension(filename, extensions):
"""
給定一個文件名filename,判斷文件名是否以擴展名列表extensions中的任意一個
Args:
filename: 文件名
extensions: 擴展名列表
Returns:
bool:給定文件是否以擴展名列表中的一個擴展名結尾
"""
for extension in extensions:
if filename.endswith(extension):
return True
return False
def check_content(text, origin_strings):
"""
檢查給定的文本text中,是否包含待替換字符串列表origin_strings中的若干項
Args:
text: 被檢查的文本
origin_strings: 待替換字符串列表
Returns:
list: 文本中包含的待替換字符串列表
"""
ret = []
# 逐個查找origin_string
for origin_string in origin_strings:
# 如果找到了,就加入到返回列表中
if origin_string in text:
ret.append(origin_string)
return ret
def read_file(path):
"""
給定路徑path,讀取對應的文本文件內容
Args:
path: 文件路徑
Returns:
str: 文件內容
"""
with open(path, 'r', encoding=ENCODE_TYPE) as f:
text = f.read()
return text
def dfs_find(path, origin_strings, filename_extensions):
"""
遞歸查找包含有origin_strings中的任意一個字符串的全部文本路徑
Args:
path: 當前目錄路徑
origin_strings: 待替換的原始字符串列表
filename_extensions: 文件擴展名列表,只對擴展名在列表中的文件進行查找
"""
# 如果給定路徑不是目錄,就直接跳過
if not os.path.isdir(path):
return
# 獲取路徑下的全部文件和子目錄
for obj in os.listdir(path):
abs_obj_path = os.path.abspath(os.path.join(path, obj))
# 如果是目錄,就遞歸查找
if os.path.isdir(abs_obj_path):
dfs_find(abs_obj_path, origin_strings, filename_extensions)
# 是文件,並且擴展名正確
elif check_filename_extension(abs_obj_path, filename_extensions):
# 先讀取文件
text = read_file(abs_obj_path)
# 然後判斷文本中是否包含指定字符串
ret_strings = check_content(text, origin_strings)
if ret_strings:
logging.info('找到一個需要進行替換的文件 {},待替換詞爲 {}'.format(abs_obj_path, ret_strings))
check_results.append((abs_obj_path, ret_strings))
# 先查找包含待替換字符串的文件路徑
logging.info('開始查找需要替換的文件...')
dfs_find(ROOT_PATH, origin_strings=ORIGIN_STRING_LIST,
filename_extensions=FILENAME_EXTENSIONS)
# 等待確認
logging.info('查找完成,共有如下文件需要進行替換,請確認!(10s後將自動開始執行)')
time.sleep(1)
pprint(check_results)
time.sleep(10)
logging.info('開始替換...')
# 開始替換
for path, origin_strings in check_results:
logging.info('正在替換 {}'.format(path))
# 先讀取
text = read_file(path)
# 再替換
for origin_string in origin_strings:
text = text.replace(origin_string, TARGET_STRING)
# 再寫入
with open(path, 'w', encoding=ENCODE_TYPE) as f:
f.write(text)
logging.info('完畢!')
請注意,在執行腳本前,請確認你清楚腳本的執行結果,最好先對數據備份。請謹慎執行。