いやその、このあいだディレクトリ単位でテキスト置換をするツールをがんばって作ったじゃあないですか。その後、「ようし、次は大文字小文字区別のオンオフ機能をつけたり、してみよう」とかわくわくしてたわけですよ。その一環としてネットで調べ物をしていたら、その機能って今使ってるエディタにデフォルトでついてたのな…。実際に使ってみたらヒジョ~に使いやすく高速で、わくわくも高速でぶっ飛んでいったんですね。けれどまあ、せっかく思いついた目標なので「コーディングの練習になるからいいんです(震え声)」と書いたのが今回のブツ。

こんな風に設定すると、

こんな風に出る。細かいテストはしてないんでいきなり実戦投入はやめたほうが吉。sublime text3のgrep機能使ったほうが全然いいぜ。俺もそっちを使うしな! じゃあなんで作ったのかって? 作るのが楽しいからですよ。

# トップレベルファイル my_grep.py

RE   = 0   # 1にすると正規表現ON
CS   = 0   # 1にすると大文字小文字区別ON
FIND = 1   # 1にすると検索結果を一覧に出す
REPL = 0   # 1にすると置換する
NAME = 1   # 1にするとファイル名・フォルダ名も検索あるいは置換する

DIR  = "C:/python/my_grep実験用"   # 末尾にスラッシュ入れないこと
FW   = "real"
RW   = ""
EXT  = [""]   # 対象にしないファイル拡張子

import os, re
import sys, traceback
import my_grep_searchFile as s
import my_grep_replaceFile as r

def makeDirs():
    ### 全ディレクトリのパスをdirsに格納する ###
    dirs = []
    dirs.append(DIR)
    for directory in dirs:
        files = os.listdir(directory)
        for f in files:
            path = directory + "/" + f
            if os.path.isdir(path):
                dirs.append(path)
    return dirs

def checkExt(path):
    ### ハブく拡張子だったらTrueを返す ###
    root, ext = os.path.splitext(path)
    if ext in EXT:
        return True
    else:
        return False

### FINDが1だったときやること ###
def find(dirs):
    finds   = []
    errorsF = []
    for directory in dirs:
        things = os.listdir(directory)
        for thing in things:
            path = directory + "/" + thing
            try:
                if not checkExt(path):                   # 指定した拡張子が含まれてなければ続行
                    find = s.searchFile(thing, path)     # 見つからないときはNoneが返ってくる
                    if find:                             # ワードが見つかったなら続行
                        find = find[:-1]
                        finds.append(find)
            except Exception as e:
                # この部分は、このスクリプトそのものをデバックするときONにしてね
                info = sys.exc_info()
                tbinfo = traceback.format_tb(info[2])
                for tbi in tbinfo:
                    print(info[1])
                    print(tbi)
                exit()
                # ここまでね
                errorsF.append("%s\n    %s" % (path, e))
    print("検索結果は")
    if not finds:
        print("アリマセン。")
    else:
        for find in finds:
            print(find)
    print("\n検索でエラーが出たファイルは")
    if not errorsF:       print("アリマセン。")
    for error in errorsF: print(error)

### REPLが1だったときやること ###
def repl(dirs):
    dirs.reverse()
    repls   = []
    errorsR = []
    for directory in dirs:
        things = os.listdir(directory)
        for thing in things:
            path = directory + "/" + thing
            try:
                if not checkExt(path):
                    repl = r.replaceFile(directory, thing)
                    if repl:
                        repl = repl[:-1]
                        repls.append(repl)
            except Exception as e:
                # この部分は、このスクリプトそのものをデバックするときONにしてね
                info = sys.exc_info()
                tbinfo = traceback.format_tb(info[2])
                for tbi in tbinfo:
                    print(info[1])
                    print(tbi)
                exit()
                # ここまでね
                errorsR.append("%s\n    %s" % (path, e))
    print("置換結果は")
    if not repls:
        print("アリマセン。")
    else:
        repls.reverse()
        for repl in repls:
            print(repl)
    print("\n置換でエラーが出たファイルは")
    if not errorsR:
        print("アリマセン。")
    else:
        errorsR.reverse()
        for error in errorsR:
            print(error)

def main():
    ### これがトップレベル関数だよ ###
    dirs    = makeDirs()
    if FIND == 1:
        find(dirs)
    if REPL == 1:
        repl(dirs)

if __name__ == "__main__":
    main()
# my_grep_searchFile.py

from my_grep import *

def searchName(thing):
    ### ファイル名に検索語が含まれてたらTrueを返す ###
    if RE == 1:
        if CS == 0:
            reFW = re.compile(FW, re.IGNORECASE)
            if re.findall(reFW, thing):
                return True                     # 正規表現使う 大文字小文字区別なし
        else:
            if re.search(FW, thing):
                return True                     # 正規表現使う 大文字小文字区別する
    else:
        if CS == 0:
            reFW = re.compile(FW, re.IGNORECASE)
            if re.findall(reFW, thing):
                return True                     # 正規表現使わない 大文字小文字区別なし
        else:
            if FW in thing:
                return True                     # 正規表現使わない 大文字小文字区別する

def searchText(find, path, yeah):
    ### ファイルのテキストに検索語が含まれてたらパスとその行が書かれた文字列を返す ###
    fopen = open(path, encoding="utf-8")
    lines = fopen.readlines()
    fopen.close()
    for i in range(len(lines)):
        if RE == 1:
            if CS == 0:
                reFW = re.compile(FW, re.IGNORECASE)
                if re.findall(reFW, lines[i]):
                    find += "    [%d] %s" % (i+1, lines[i])
                    yeah  = True                # 正規表現使う 大文字小文字区別なし
            else:
                if re.search(FW, lines[i]):
                    find += "    [%d] %s" % (i+1, lines[i])
                    yeah  = True                # 正規表現使う 大文字小文字区別する
        else:
            if CS == 0:
                reFW = re.compile(FW, re.IGNORECASE)
                if re.findall(reFW, lines[i]):
                    find += "    [%d] %s" % (i+1, lines[i])
                    yeah  = True                # 正規表現使わない 大文字小文字区別なし
            else:
                if lines[i].find(FW) >= 0:
                    find += "    [%d] %s" % (i+1, lines[i])
                    yeah  = True                # 正規表現使わない 大文字小文字区別する
    return(find, yeah)

def searchFile(thing, path):
    ### もし検索語が含まれてたら、パスとその行が書かれた文字列を返す ###
    yeah = False
    find = "%s\n" % path
    if NAME == 1:                       # ファイル名検索するなら続行する
        yeah = searchName(thing)
    if os.path.isfile(path):            # それがファイルなら開いて検索する
        find, yeah = searchText(find, path, yeah)
    if yeah == True:
        return find
# my_grep_replaceFile.py

from my_grep import *

def replaceName(directory, thing, yeah):
    ### ファイル名に検索語が含まれてたら、置換してTrueを返す ###
    if RE == 1:
        if CS == 0:
            reFW = re.compile(FW, re.IGNORECASE)
            if re.findall(reFW, thing):
                yeah = True                     # 正規表現使う 大文字小文字区別なし
        else:
            if re.search(FW, thing):
                yeah = True                     # 正規表現使う 大文字小文字区別する
    else:
        if CS == 0:
            reFW = re.compile(FW, re.IGNORECASE)
            if re.findall(reFW, thing):
                yeah = True                     # 正規表現使わない 大文字小文字区別なし
        else:
            if FW in thing:
                yeah = True                     # 正規表現使わない 大文字小文字区別する
    if yeah == True:
        newthing = thing.replace(FW, RW)
        os.rename(directory + "/" + thing, directory + "/" + newthing)
    else:
        newthing = thing
    return newthing, yeah

def replaceText(repl, directory, thing, yeah):
    ### もし検索語が含まれてたら、置換して、パスとその行が書かれた文字列を返す ###
    path  = directory + "/" + thing
    fopen = open(path, encoding="utf-8")
    lines = fopen.readlines()
    fopen.close()
    for i in range(len(lines)):
        if RE == 1:
            if CS == 0:
                reFW = re.compile(FW, re.IGNORECASE)
                if re.findall(reFW, lines[i]):
                    for element in re.findall(reFW, lines[i]):
                        lines[i] = lines[i].replace(element, RW)
                    repl    += "    [%d] %s" % (i+1, lines[i])
                    yeah     = True                # 正規表現使う 大文字小文字区別なし
            else:
                if re.search(FW, lines[i]):
                    lines[i] = lines[i].replace(FW, RW)
                    repl    += "    [%d] %s" % (i+1, lines[i])
                    yeah     = True                # 正規表現使う 大文字小文字区別する
        else:
            if CS == 0:
                reFW = re.compile(FW, re.IGNORECASE)
                if re.findall(reFW, lines[i]):
                    for element in re.findall(reFW, lines[i]):
                        lines[i] = lines[i].replace(element, RW)
                    repl    += "    [%d] %s" % (i+1, lines[i])
                    yeah     = True                # 正規表現使わない 大文字小文字区別なし
            else:
                if lines[i].find(FW) >= 0:
                    lines[i] = lines[i].replace(FW, RW)
                    repl    += "    [%d] %s" % (i+1, lines[i])
                    yeah     = True                # 正規表現使わない 大文字小文字区別する
    if yeah == True:
        ### 置換したものを上書き処理 ###
        temp = ""
        for i in range(len(lines)):
            temp += lines[i]
        fopen = open(path, "w", encoding="utf-8")
        fopen.write(temp)
        fopen.close()
    return repl, yeah

def replaceFile(directory, thing):
    ### もし検索語が含まれてたら、パスとその行が書かれた文字列を返す ###
    yeah = False
    path = directory + "/" + thing
    repl = "%s\n" % path
    if NAME == 1:                                   # ファイル名検索するなら続行する
        thing, yeah = replaceName(directory, thing, yeah)
        path = directory + "/" + thing              # ファイル開くためにrename済みのパスを作っとく
        repl = "%s\n" % path                        # 置換済みのパスを記述しとく
    if os.path.isfile(path):                        # それがファイルなら開いて検索する
        repl, yeah = replaceText(repl, directory, thing, yeah)
    if yeah == True:
        return repl

最近ちょこちょこ読んでいるオライリーpythonの記述が結構利用できて満足だ。具体的には、関数の大きさを一画面に収まる程度にするよう心がけたり、インポートファイル側からトップレベルファイルのグローバル変数を利用したいときすぐに名前空間の共有を思いつけて手早く問題をクリアーできたり、だ。