views:

284

answers:

3

How can you replace the match with the given replacement recursively in a given directory and its subdirectories?

Pseudo-code

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
+2  A: 

Do you really need regular expressions?

import os

def recursive_replace( root, pattern, replace )
    for dir, subdirs, names in os.walk( root ):
        for name in names:
            path = os.path.join( dir, name )
            text = open( path ).read()
            if pattern in text:
                open( path, 'w' ).write( text.replace( pattern, replace ) )
kurosch
Globbing seems to be betters than regex in this case.
Masi
Does 'root' here really mean the lowest level of the directory hierarchy? Or is it only a variable where you can store the Path to be searched?
Masi
@Masi: "root" is just a parameter name, the lowest level directory to start searching from. you can name it anything you like
kurosch
+5  A: 

Put all this code into a file called mass_replace and chmod +x mass_replace:

#!/usr/bin/python

import os
import re
import sys

def file_replace(fname, s_before, s_after):
    out_fname = fname + ".tmp"
    out = open(out_fname, "w")
    for line in open(fname):
        out.write(re.sub(s_before, s_after, line))
    out.close()
    os.rename(out_fname, fname)


def mass_replace(dir_name, s_before, s_after):
    for dirpath, dirnames, filenames in os.walk(dir_name):
        for fname in filenames:
            f = fname.lower()
            # example: limit replace to .txt, .c, and .h files
            if f.endswith(".txt") or f.endswith(".c") or f.endswith(".h"):
                fullname = os.path.join(dirpath, fname)
                file_replace(fullname, s_before, 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])
steveha
I like the logic of your code in separating the logical parts to functions.
Masi
+1  A: 

Of course, if you just want to get it done without coding it up, use find and xargs:

find /home/noa/Desktop/codes -type f -print0 | \
xargs -0 sed --in-place "s/dbname=noa user=noa/dbname=masi user=masi"

(And you could likely do this with find's -exec or something as well, but I prefer xargs.)

retracile
The find and sed solution is fine when you have a simple task, such as "replace the string in every *.txt file". Once you have a more complicated set of files to match, and if you have multiple replacements to do, the Python solution really wins.
steveha