python - 用 Python 在给定目录及其子目录中递归替换文件中的字符串?

标签 python regex search replace operating-system

如何在给定目录及其子目录中递归地用给定的替换替换文件中的字符串匹配?

伪代码:

import os
import re
from os.path import walk
for root, dirs, files in os.walk("/home/noa/Desktop/codes"):
        for name in dirs:
                re.search("dbname=noa user=noa", "dbname=masi user=masi")
                   // I am trying to replace here a given match in a file

最佳答案

将所有这些代码放入一个名为 mass_replace 的文件中。在 Linux 或 Mac OS X 下,您可以执行 chmod +x mass_replace 然后运行它。在 Windows 下,您可以使用 python mass_replace 后跟适当的参数来运行它。

#!/usr/bin/python

import os
import re
import sys

# list of extensions to replace
DEFAULT_REPLACE_EXTENSIONS = None
# example: uncomment next line to only replace *.c, *.h, and/or *.txt
# DEFAULT_REPLACE_EXTENSIONS = (".c", ".h", ".txt")

def try_to_replace(fname, replace_extensions=DEFAULT_REPLACE_EXTENSIONS):
    if replace_extensions:
        return fname.lower().endswith(replace_extensions)
    return True


def file_replace(fname, pat, s_after):
    # first, see if the pattern is even in the file.
    with open(fname) as f:
        if not any(re.search(pat, line) for line in f):
            return # pattern does not occur in file so we are done.

    # pattern is in the file, so perform replace operation.
    with open(fname) as f:
        out_fname = fname + ".tmp"
        out = open(out_fname, "w")
        for line in f:
            out.write(re.sub(pat, s_after, line))
        out.close()
        os.rename(out_fname, fname)


def mass_replace(dir_name, s_before, s_after, replace_extensions=DEFAULT_REPLACE_EXTENSIONS):
    pat = re.compile(s_before)
    for dirpath, dirnames, filenames in os.walk(dir_name):
        for fname in filenames:
            if try_to_replace(fname, replace_extensions):
                fullname = os.path.join(dirpath, fname)
                file_replace(fullname, pat, s_after)

if len(sys.argv) != 4:
    u = "Usage: mass_replace <dir_name> <string_before> <string_after>\n"
    sys.stderr.write(u)
    sys.exit(1)

mass_replace(sys.argv[1], sys.argv[2], sys.argv[3])

编辑:我已经从原来的答案中更改了上面的代码。有几个变化。首先,mass_replace() 现在调用 re.compile() 来预编译搜索模式;其次,为了检查文件的扩展名,我们现在将文件扩展名的元组传递给 .endswith() 而不是调用 .endswith() 三次;第三,它现在使用最新版本的 Python 中提供的 with 语句;最后,file_replace() 现在检查是否在文件中找到了模式,如果没有找到模式则不会重写文件。 (旧版本会重写每个文件,更改时间戳,即使输出文件与输入文件相同;这是不雅的。)

编辑:我将其更改为默认替换每个文件,但是您可以编辑一行以将其限制为特定的扩展名。我认为替换每个文件是一个更有用的开箱即用的默认设置。这可以通过不涉及的扩展名或文件名列表、使其不区分大小写的选项等进行扩展。

编辑:在评论中,@asciimo 指出了一个错误。我对此进行了编辑以修复错误。 str.endswith() 记录为接受要尝试的字符串元组,但不接受列表。固定的。另外,我让几个函数接受一个可选参数,让你传入一个扩展元组;修改它以接受指定扩展名的命令行参数应该很容易。

关于python - 用 Python 在给定目录及其子目录中递归替换文件中的字符串?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1597649/

相关文章:

mysql - mysql搜索性能

python - 删除python中的空格

python - 列表相对于另一列的频率分布

python - 将 self.user 作为外键返回

python - 正则表达式如何处理正则表达式模式中间的 `^` 或 `$`?

grails - 使用 Google 搜索代码中常见的字符串

Python 终止进程,退出代码为 -1073741819

regex - 如何用不在数字之间的斜杠分割字符串?

android - 为什么 Android 的 "not the following character"正则表达式是 [!x] 而不是 [^x]?

c# - 如何找到最接近任意(非成员)数字的数组元素?