Pythonクックブック(2章)

ファイル操作の続き

ディレクトリの探索(os.walk, fnmatch)

os.walk(top)を実行すると、(dirpath, subdirnames, filenames) を返す。

[kobakoba0723@fedora13-intel64 ~]$ ls -R top/
top/:
eggs.txt  spam.txt  sub

top/sub:
hoge.data  piyo.data

>>> import os
>>> for (dirpath, subdirs, files) in os.walk('top'):
...     print dirpath
...     print files, subdirs
... 
top
['spam.txt', 'eggs.txt'] ['sub']
top/sub
['piyo.data', 'hoge.data'] []
>>> 

fnmatchは シェルのワイルドカードを使ったファイル名のマッチングをするモジュール。
fnmatch.fnmatch(name, pattern)はファイル名nameが パターンpattern にマッチした場合 True を返す

この2つを組み合わせると find | grep と同じようなことが出来る。

[kobakoba0723@fedora13-intel64 ~]$ find top/ | grep -e '.*\.txt' -e '.*\.data'
top/spam.txt
top/eggs.txt
top/sub/piyo.data
top/sub/hoge.data
[kobakoba0723@fedora13-intel64 ~]$ 

fnmatchはシェルのワイルドカードを解釈し、grep正規表現を解釈するので実行時の引数が違う。

[kobakoba0723@fedora13-intel64 ~]$ cat findgrep.py 
import os
import fnmatch


def find_grep(root, patterns='*'):
    patterns = patterns.split(';')
    for (dirpath, subdirs, files) in os.walk(root):
        files.sort()
        for name in files:
            for pattern in patterns:
                if fnmatch.fnmatch(name, pattern):
                    yield os.path.join(dirpath, name)
                    break


if __name__ == '__main__':
    import sys
    if len(sys.argv) != 3:
        print 'Usage: python findgrep.py <top> <pattern>'
        sys.exit(1)
    for filename in find_grep(sys.argv[1], sys.argv[2]):
        print filename
[kobakoba0723@fedora13-intel64 ~]$ python findgrep.py 'top' '*.txt;*.data'
top/eggs.txt
top/spam.txt
top/sub/hoge.data
top/sub/piyo.data
[kobakoba0723@fedora13-intel64 ~]$ 

拡張子の変更(os.walk, os.rename)

os.rename(src, dst) は mv src dst と同じ。
os.walk と os.rename を組み合わせると、シェルの forループ + mv と同じことが出来る。

[kobakoba0723@fedora13-intel64 ~]$ ls -R top.shell/
top.shell/:
eggs.txt  spam.txt  sub

top.shell/sub:
hoge.data  piyo.data
[kobakoba0723@fedora13-intel64 ~]$ for file in $(find top.shell/ -type f | grep .*\.txt)
> do
>   mv $file ${file%%.txt}.data
> done
[kobakoba0723@fedora13-intel64 ~]$ ls -R top.shell/
top.shell/:
eggs.data  spam.data  sub

top.shell/sub:
hoge.data  piyo.data
[kobakoba0723@fedora13-intel64 ~]$
[kobakoba0723@fedora13-intel64 ~]$ cat swapextension.py 
import os


def swap_extension(dir, before, after):
    if before[:1] != '.':
        before = '.' + before
    extension_len = -len(before)
    if after[:1] != '.':
        after = '.' + after
    for (dirpath, subdirs, files) in os.walk(dir):
        for filename in files:
            old_filepath = os.path.join(dirpath, filename)
            new_filepath = old_filepath[:extension_len] + after
            os.rename(old_filepath, new_filepath)


if __name__ == '__main__':
    import sys
    if len(sys.argv) != 4:
        print "Usage: python swapextension.py <rootdir> <before> <after>"
        sys.exit(1)
    swap_extension(sys.argv[1], sys.argv[2], sys.argv[3])
[kobakoba0723@fedora13-intel64 ~]$ ls -R top.py/
top.py/:
eggs.txt  spam.txt  sub

top.py/sub:
hoge.data  piyo.data
[kobakoba0723@fedora13-intel64 ~]$ python swapextension.py top.py '.txt' '.data' 
[kobakoba0723@fedora13-intel64 ~]$ ls -R top.py/
top.py/:
eggs.data  spam.data  sub

top.py/sub:
hoge..data  piyo..data
[kobakoba0723@fedora13-intel64 ~]$ 

サーチパスからファイルを探す(os.path)

os.path モジュールはプラットフォームに依存しない共通のパス名操作モジュール
このモジュールを使うと、which コマンドと同じようなことが出来る。

[kobakoba0723@fedora13-intel64 ~]$ which pep8
~/bin/pep8
[kobakoba0723@fedora13-intel64 ~]$ which pep
/usr/bin/which: no pep in (/home/kobakoba0723/bin:/usr/kerberos/sbin:/usr/kerberos/bin:/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin)
[kobakoba0723@fedora13-intel64 ~]$ 

今の状態はenv_pathを固定でやってしまっているので、
シェル変数$PATHから値を持って来れるようになれば良いんだけどなぁ。

[kobakoba0723@fedora13-intel64 ~]$ cat searchcmd.py 
import os


def search_command(command, search_path, path_sep=os.pathsep):
    for path in search_path.split(os.pathsep):
        candidate = os.path.join(path, command)
        if os.path.isfile(candidate):
            return os.path.abspath(candidate)


if __name__ == '__main__':
    import sys
    if len(sys.argv) != 2:
        print 'Usage: python searchcmd.py <command>'
        sys.exit(1)
    search_cmd = sys.argv[1]
    env_path = ['/home/kobakoba0723/bin', '/usr/local/bin',
                '/usr/bin', '/usr/local/sbin', '/usr/sbin', '/sbin']
    search_path = os.pathsep.join(env_path)
    cmd_path = search_command(search_cmd, search_path)
    if cmd_path:
        print "Command '%s' found in '%s'" % (search_cmd, cmd_path)
    else:
        print "Command '%s' not found in '%s'" % (search_cmd, search_path)
[kobakoba0723@fedora13-intel64 ~]$ python searchcmd.py pep8
Command 'pep8' found in '/home/kobakoba0723/bin/pep8'
[kobakoba0723@fedora13-intel64 ~]$ python searchcmd.py pep
Command 'pep' not found in '/home/kobakoba0723/bin:/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/sbin'
[kobakoba0723@fedora13-intel64 ~]$ 

参考サイト

モジュールインデックス@Python 2.6.2 document

Python クックブック 第2版

Python クックブック 第2版