1 つの問題は、存在のテストとファイルの作成の間にギャップがあるため、上記のコードに競合状態があることです。これにはセキュリティ上の影響があるかもしれません (誰かが悪意を持って重要なファイルにシンボリックリンクを挿入して上書きすることはできませんが、より高い特権で実行されているプログラムは可能です) これらのような攻撃が os.tempnam( ) は非推奨です。
これを回避するための最善の方法は、実際にファイルを作成してみて、失敗した場合に例外が発生し、成功した場合に実際に開かれたファイル オブジェクトを返すことです。これは、os.O_CREAT フラグと os.O_EXCL フラグの両方を渡すことにより、下位レベルの os.open 関数で実行できます。開いたら、作成した実際のファイル (およびオプションでファイル名) を返します。たとえば、このアプローチを使用するように変更されたコードは次のとおりです ((ファイル、ファイル名) タプルを返します)。
def unique_file(file_name):
counter = 1
file_name_parts = os.path.splitext(file_name) # returns ('/path/file', '.ext')
while 1:
try:
fd = os.open(file_name, os.O_CREAT | os.O_EXCL | os.O_RDRW)
return os.fdopen(fd), file_name
except OSError:
pass
file_name = file_name_parts[0] + '_' + str(counter) + file_name_parts[1]
counter += 1
[編集] 実際には、上記の問題を処理するためのより良い方法は、おそらく tempfile モジュールを使用することですが、名前付けを制御できなくなる可能性があります。これを使用する例を次に示します (同様のインターフェースを維持します)。
def unique_file(file_name):
dirname, filename = os.path.split(file_name)
prefix, suffix = os.path.splitext(filename)
fd, filename = tempfile.mkstemp(suffix, prefix+"_", dirname)
return os.fdopen(fd), filename
>>> f, filename=unique_file('/home/some_dir/foo.txt')
>>> print filename
/home/some_dir/foo_z8f_2Z.txt
このアプローチの唯一の欠点は、変更されていないファイル (/home/some_dir/foo.txt) を最初に作成しようとしないため、ランダムな文字が含まれるファイル名が常に取得されることです。tempfile.TemporaryFile と NamedTemporaryFile も参照してください。これらは上記を実行し、閉じたときにディスクからも自動的に削除します。