使用Python腳本批量替換項目中的數據庫地址

這幾天接手到了一個很老很老的項目,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('完畢!')

請注意,在執行腳本前,請確認你清楚腳本的執行結果,最好先對數據備份。請謹慎執行。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章