2

私は私が望むものを除いてすべてをやっている以下のコードを持っています:-)。目標は単純です。ディレクトリ(およびサブディレクトリ)に存在する各ファイルの先頭にあるappendtext変数にテキストを追加しようとしています。スクリプトは正常に実行されますが、テキストは追加されません。どこが間違っているのですか?

import os
import sys
import fnmatch
temp_fname = "temp_file"

appendtext="""Test string
"""
if len(sys.argv) < 2:
    sys.exit('Usage: test.py <build directory>')

for path,dirs,files in os.walk(sys.argv[1]):
    for fname in files:
        for pat in ['*.*']:
            if fnmatch.fnmatch(fname,pat):
                fullname = os.path.join(path,fname)
                with open(fullname, "r") as in_file:
                    with open(temp_fname, "w") as out_file:
                        out_file.write(appendtext)
                        for line in in_file:
                            out_file.write(line)
                os.rename(temp_fname, fullname)
4

2 に答える 2

2

ファイルの先頭にテキストが必要な場合は、次のようにする必要があります。

temp_fname = "temp_file"
# the next line doesn't work in Python 2.5, 2.6, or 3.0
with open(fullname, "r") as in_file, open(temp_fname, "w") as out_file:
    out_file.write(appendtext)
    for line in in_file:
        out_file.write(line)
os.rename(temp_fname, fullname)

上記をPython2.6用に書き直しました。

temp_fname = "temp_file"
with open(temp_fname, "w") as out_file:
    with open(fullname, "r") as in_file:
        out_file.write(appendtext)
        for line in in_file:
            out_file.write(line)
os.rename(temp_fname, fullname)

これより少し上手くできます。これは常に同じ一時ファイル名("temp_file")を使用し、そのファイルは常に単一のディレクトリ(これを実行するときのデフォルトディレクトリ)に作成されます。本当に必要なのは、編集するファイルとまったく同じディレクトリに作成された、一意の名前の一時ファイルです。tempfilePythonには、一時ファイルを作成するという便利なモジュールが用意されています。

デフォルトでは、開いているファイルハンドルを取得するだけで、ファイル名はわかりません。ただし、一時コピーが完全に完了した後、元のファイル名に名前を変更できるように、ファイル名を知る必要があります。 tempfileこのようなNamedTemporaryFile場合に備えています。

これが完全なプログラムです:

import fnmatch
import os
import sys
import tempfile

headertext = "# header text\n\n"

def want_this_file(fname):
    for pat in ['*']:
        if fnmatch.fnmatch(fname, pat):
            return True
    return False

def prepend_file(fullname, path):
    # with statement means temp file is written and closed at end of with
    with tempfile.NamedTemporaryFile(dir=path, delete=False) as out_file:
        with open(fullname, "r") as in_file:
                out_file.write(headertext)
                for line in in_file:
                    out_file.write(line)
        # before temp file is closed, get its name
        temp_fname = out_file.name
    # rename temp file to fullname, clobbering original
    os.rename(temp_fname, fullname)


start_directory = sys.argv[1]

for dirpath, dirnames, filenames in os.walk(start_directory):
    for fname in filenames:
        if want_this_file(fname):
            fullname = os.path.join(dirpath, fname)
            prepend_file(fullname, dirpath)

この回答では、「一時ファイルを作成してから、一時ファイルの名前を元の名前に変更する」というパターンを使用しています。これはあなたがそれをするべき方法です。これにより、コードは新しいバージョンを書き込むことができ、新しいバージョンが完全に正常に書き込まれた場合にのみ単一のアクションを実行して新しいファイルの名前を古いファイル名に変更します。したがって、新しいバージョンを書き込もうとしているときに問題が発生した場合、元のファイルは変更されません。これは問題を解決するための安全な方法です。

元のファイルと同じディレクトリに一時ファイルを作成して、os.rename()操作が簡単にできるようにします。Linuxシステムでは、システムの一時ディレクトリ(/tmp)が独自のパーティションにある可能性がtempfileあり、そこに一時ファイルを作成するだけの場合、名前変更操作にはデータの再コピーが含まれる可能性があります。一時ファイルが同じディレクトリにある場合、名前変更操作は常に非常に高速で安全です。

編集:これはコードの改良版です。これによりエラーがキャッチされ、エラーを通知する例外を再発生させる前に一時ファイルがクリーンアップされます。また、JFセバスティアンが指摘したように、ファイルはバイナリモードで開く必要があります。これはそれを行います。

import fnmatch
import os
import shutil
import sys
import tempfile

file_patterns_to_match = ['*']

headertext = "# header text\n\n"
# make any newlines in headertext match the system line ending
headertext = headertext.replace('\n', os.linesep)

def want_this_file(fname):
    for pat in file_patterns_to_match:
        if fnmatch.fnmatch(fname, pat):
            return True
    return False

def prepend_file(fullname, path):
    # with statement means temp file is written and closed at end of with
    with tempfile.NamedTemporaryFile(dir=path, delete=False) as out_file:
        # get the name immediately
        temp_fname = out_file.name

        try:
            # use binary mode to avoid newline translations
            with open(fullname, "rb") as in_file:
                out_file.write(headertext)
                shutil.copyfileobj(in_file, out_file)
        except Exception:
            # on any error, clean up temp file and re-raise exception
            try:
                os.remove(temp_fname)
            except Exception:
                print("unable to clean up temp file: " + temp_fname)
                pass
            raise
    # rename temp file to fullname, clobbering original
    os.rename(temp_fname, fullname)


start_directory = sys.argv[1]

for dirpath, dirnames, filenames in os.walk(start_directory):
    for fname in filenames:
        if want_this_file(fname):
            fullname = os.path.join(dirpath, fname)
            prepend_file(fullname, dirpath)
于 2013-01-09T02:06:03.513 に答える
0

私はあなたの質問があなたのPythonスクリプトについてであることを知っています。しかし、perlを使用してbashのワンライナーでこれを達成できます。

sgeorge-mn:stack sgeorge$ cat file_0*
Hi - 1 
Hi - 2 
Hi - 3 
Hi - 4 
Hi - 5 

sgeorge-mn:stack sgeorge$ find . -type f
./file_01.txt
./file_02.txt
./file_03.txt
./file_04.txt
./file_05.txt

sgeorge-mn:stack sgeorge$ find . -type f -exec  perl -pi -e 'print "Please donot add any comments \nThis is a header file.\nJust a test.\n" if $. == 1' {} \;

sgeorge-mn:stack sgeorge$ cat file_0*
Please donot add any comments 
This is a header file.
Just a test.
Hi - 1 
Please donot add any comments 
This is a header file.
Just a test.
Hi - 2 
Please donot add any comments 
This is a header file.
Just a test.
Hi - 3 
Please donot add any comments 
This is a header file.
Just a test.
Hi - 4 
Please donot add any comments 
This is a header file.
Just a test.
Hi - 5 
于 2013-01-09T02:22:47.537 に答える