ファイルの変更を監視するにはどうすればよいですか?


323

変更を監視したい別のプロセスによってログファイルが書き込まれています。変更が発生するたびに、新しいデータを読み込んで処理を行います。

これを行う最良の方法は何ですか?PyWin32ライブラリから何らかのフックが存在することを期待していました。win32file.FindNextChangeNotification関数を見つけましたが、特定のファイルを監視するように要求する方法がわかりません。

誰かがこのようなことをしたなら、私はその方法を聞いて本当に感謝します...

[編集]私はポーリングを必要としないソリューションを求めていたと述べるべきだった。

[編集]呪い!これは、マップされたネットワークドライブでは機能しないようです。Windowsがローカルディスク上で行うようにファイルへの更新を「聞く」ことはないと思います。


1
Linuxでは、このためにstraceモニタリングwrite呼び出しを使用できます
test30 '30

@simaoの回答ではpython-watchdogを使用しています。Python-Watchdogにはすばらしいドキュメントがあります->現在の作業ディレクトリを監視する最小限のコード例を提供する["QuickStart"]ドキュメントへのリンクです。
Trevor Boyd Smith

回答:


79

http://timgolden.me.uk/python/win32_how_do_i/watch_directory_for_changes.htmlで入手可能なドキュメントをすでに見ましたか?Windowsでのみ機能する必要がある場合、2番目の例はまさに望んでいるようです(ディレクトリのパスを監視するファイルの1つと交換する場合)。

そうでなければ、ポーリングはおそらく唯一の本当にプラットフォームに依存しないオプションになります。

注:これらの解決策はまだ試していません。


5
この回答はWindows固有ですが、この問題に対するいくつかのクロスプラットフォームソリューションがここにも投稿されているようです。
アンダーソングリーン

ベンチマークはありますか?このプロセスが遅い場合、c ++のようなネイティブ言語で実装するより遅いですか?
user1767754 2014年

古くなっている可能性があるため、引用されたソースから関連するコンテンツを挿入することをお勧めします。
Trilarion 2017

2
(1.)この回答の最後には強い免責事項があります...「私はこれらの解決策のいずれも試していません」。(2.)この回答は多かれ少なかれ「リンクのみ」の回答です(3.)回答は「ポーリング」について言及していますが、その後に役立つ情報はありません... @Deestanの回答はポーリングに関するいくつかの優れた情報を提供するため
Trevor Boyd Smith、

283

Watchdogを使用してみましたか?

ファイルシステムイベントを監視するPython APIライブラリとシェルユーティリティ。

でディレクトリ監視を簡単に

  • クロスプラットフォームAPI。
  • ディレクトリの変更に応じてコマンドを実行するシェルツール。

クイックスタートの簡単な例ですぐに始めましょう...


56
easy_install?でインストール可能 小切手。無料ライセンス?確認してください。大きなプラットフォームで問題を解決しますか?確認してください。私はこの答えを支持します。注:プロジェクトページの例はそのままでは機能しません。代わりにgithubあるものを使用してください。
Inaimathi

6
ウォッチドッグを使用しています。QFileSystemWatcherに切り替える場合があります。公正な警告-ウォッチドッグは良いですが、すべてのプラットフォームで(現時点では)完全ではありません。各OSには特異性があります。だから、あなたがそれを完璧にすることに専念していない限り、あなたはあなたの髪を引き出します。10個程度のファイルを見たいだけの場合は、投票します。OSのディスクキャッシングは非常に成熟しており、WatchdogはとにかくAPIのポーリングを行います。それは主に巨大なフォルダー構造の私見を監視するためです。
SilentSteel 2013年

3
ウォッチドッグに関する私の1つの不満は、多くの依存関係があることです。もちろん、PyQtよりも少ないですが、それは機能せず、最小限のベストプラクティスであり、1つのジョブで1つのジョブを実行して適切なソリューションであるように感じます。
AndreasT 2014年

1
@denfromufaはここで正しいですか?ウォッチドッグは本当にファイルをロックするので、ファイルを同時に監視してウォッチドッグすることはできませんか?信じられないほど、それは完全に役に立たないでしょう。
ミシェル・ミューラー

1
@MichelMüller私はこの例(下のリンクを参照)をチェックしたところ、うまくいきました!以前は何が悪かったのかはわかりませんが、この回答には例がありません。stackoverflow.com/a/18599427/2230844
denfromufa

92

ポーリングで十分であれば、「変更された時間」ファイルのステータスが変化するかどうかを確認します。それを読むには:

os.stat(filename).st_mtime

(また、Windowsネイティブの変更イベントソリューションは、ネットワークドライブなど、すべての状況で機能するわけではないことに注意してください。)

import os

class Monkey(object):
    def __init__(self):
        self._cached_stamp = 0
        self.filename = '/path/to/file'

    def ook(self):
        stamp = os.stat(self.filename).st_mtime
        if stamp != self._cached_stamp:
            self._cached_stamp = stamp
            # File has changed, so do something...

1
間隔をおいてこれを行うにはどうすればよいですか?
dopatraman 2016年

1
@dopatramanこれは、間隔でこれを行う方法です `import sys import time pub = Monkey()while True:try:time.sleep(1)pub.watch()except KeyboardInterrupt:print( '\ nDone')break except :print(f'Unhandled error:{sys.exc_info()[0]} ') `
Vlad Bezden

素晴らしいシンプルなソリューション!最初の実行時に変更されたファイルを報告しないようにするためのチェックを追加しましたif self._cached_stamp is not None
ヌメノン

50

マルチプラットフォームのソリューションが必要な場合は、QFileSystemWatcherを確認してください。以下はサンプルコードです(サニタイズされていません):

from PyQt4 import QtCore

@QtCore.pyqtSlot(str)
def directory_changed(path):
    print('Directory Changed!!!')

@QtCore.pyqtSlot(str)
def file_changed(path):
    print('File Changed!!!')

fs_watcher = QtCore.QFileSystemWatcher(['/path/to/files_1', '/path/to/files_2', '/path/to/files_3'])

fs_watcher.connect(fs_watcher, QtCore.SIGNAL('directoryChanged(QString)'), directory_changed)
fs_watcher.connect(fs_watcher, QtCore.SIGNAL('fileChanged(QString)'), file_changed)

6
これは、a)Win32のFileSystemwatcherオブジェクトに依存していて移植できない、またはb)ファイルをポーリングする(パフォーマンスに悪影響を及ぼし、スケーリングされない)ことを考えると、おそらくこれが束の最良の答えだと思います。Pythonにこの機能が組み込まれていないのは残念です。なぜなら、使用しているすべてがQFileSystemWatcherクラスである場合、PyQtは大きな依存関係であるためです。
CadentOrange

4
私はこのソリューションが好きです。それが機能するためにはQApplicationインスタンスが必要であることを指摘したかったので、インポートのすぐ下に「app = QtGui.QApplication(sys.argv)」を追加し、信号接続の後に「app.exec_()」を追加しました。
スペンスワー

これをLinuxボックスでテストしたところ、file_changedではなくdirectory_changedメソッドが呼び出されていることがわかりました。
Ken Kinder

@ CadentOrange、pyQt依存関係が気に入らない場合はwatchdogパッケージが正解です
Mike Pennington

PySideそのPyQtような小さな使用の代わりにそれのために使用しないのはなぜですか。
Ciasto piekarz 2015

29

これはWindowsでは動作しないはずです(たぶんcygwinで?)。ただし、UNIXユーザーの場合は、「fcntl」システムコールを使用する必要があります。Pythonの例を次に示します。Cで記述する必要がある場合は、ほとんど同じコードです(同じ関数名)。

import time
import fcntl
import os
import signal

FNAME = "/HOME/TOTO/FILETOWATCH"

def handler(signum, frame):
    print "File %s modified" % (FNAME,)

signal.signal(signal.SIGIO, handler)
fd = os.open(FNAME,  os.O_RDONLY)
fcntl.fcntl(fd, fcntl.F_SETSIG, 0)
fcntl.fcntl(fd, fcntl.F_NOTIFY,
            fcntl.DN_MODIFY | fcntl.DN_CREATE | fcntl.DN_MULTISHOT)

while True:
    time.sleep(10000)

3
Linuxカーネル2.6.31(Ubuntu 10.04上)のLinuxカーネル2.6.31でチャームのように機能しますが、ディレクトリに対してのみです-ファイルで使用すると、「ディレクトリではない」というIOErrorが発生します。
David Underhill

1
すごい!私も同じで、ディレクトリのみで機能し、このディレクトリ内のファイルを監視します。ただし、サブディレクトリ内の変更されたファイルに対しては機能しないため、サブディレクトリ全体を調べてすべてを監視する必要があるようです。(またはこれを行うより良い方法はありますか?)
lfagundes

20

pyinotifyをチェックしてください

inotifyは、新しいlinuxの(以前の回答からの)dnotifyを置き換え、ディレクトリレベルではなくファイルレベルの監視を可能にします。


5
この答えを弱めることはしませんが、この記事を読んだ後は、思ったほど魅力的な解決策にはならないかもしれません。serpentine.com/blog/2008/01/04/why-you-should-not-use-pyinotify
NuclearPeon 2014年

1
pyinotifyには、非常に非Pythonicのコードベースからメモリ消費まで、多くの欠点があります。他のオプションを探す方が良い..
Tyto

13

Tim Goldenのスクリプトを少しハッキングした後、次のように動作します。

import os

import win32file
import win32con

path_to_watch = "." # look at the current directory
file_to_watch = "test.txt" # look for changes to a file called test.txt

def ProcessNewData( newData ):
    print "Text added: %s"%newData

# Set up the bits we'll need for output
ACTIONS = {
  1 : "Created",
  2 : "Deleted",
  3 : "Updated",
  4 : "Renamed from something",
  5 : "Renamed to something"
}
FILE_LIST_DIRECTORY = 0x0001
hDir = win32file.CreateFile (
  path_to_watch,
  FILE_LIST_DIRECTORY,
  win32con.FILE_SHARE_READ | win32con.FILE_SHARE_WRITE,
  None,
  win32con.OPEN_EXISTING,
  win32con.FILE_FLAG_BACKUP_SEMANTICS,
  None
)

# Open the file we're interested in
a = open(file_to_watch, "r")

# Throw away any exising log data
a.read()

# Wait for new data and call ProcessNewData for each new chunk that's written
while 1:
  # Wait for a change to occur
  results = win32file.ReadDirectoryChangesW (
    hDir,
    1024,
    False,
    win32con.FILE_NOTIFY_CHANGE_LAST_WRITE,
    None,
    None
  )

  # For each change, check to see if it's updating the file we're interested in
  for action, file in results:
    full_filename = os.path.join (path_to_watch, file)
    #print file, ACTIONS.get (action, "Unknown")
    if file == file_to_watch:
        newText = a.read()
        if newText != "":
            ProcessNewData( newText )

おそらく、より多くのエラーチェックをロードすることで実行できますが、単にログファイルを監視し、画面に出力する前にログファイルで何らかの処理を行う場合、これはうまく機能します。

皆様のご意見、ありがとうございます。


10

ポーリングと最小限の依存関係で単一のファイルを監視するために、Deestan(上記)からの回答に基づいて完全に具体化された例を次に示します。

import os
import sys 
import time

class Watcher(object):
    running = True
    refresh_delay_secs = 1

    # Constructor
    def __init__(self, watch_file, call_func_on_change=None, *args, **kwargs):
        self._cached_stamp = 0
        self.filename = watch_file
        self.call_func_on_change = call_func_on_change
        self.args = args
        self.kwargs = kwargs

    # Look for changes
    def look(self):
        stamp = os.stat(self.filename).st_mtime
        if stamp != self._cached_stamp:
            self._cached_stamp = stamp
            # File has changed, so do something...
            print('File changed')
            if self.call_func_on_change is not None:
                self.call_func_on_change(*self.args, **self.kwargs)

    # Keep watching in a loop        
    def watch(self):
        while self.running: 
            try: 
                # Look for changes
                time.sleep(self.refresh_delay_secs) 
                self.look() 
            except KeyboardInterrupt: 
                print('\nDone') 
                break 
            except FileNotFoundError:
                # Action on file not found
                pass
            except: 
                print('Unhandled error: %s' % sys.exc_info()[0])

# Call this function each time a change happens
def custom_action(text):
    print(text)

watch_file = 'my_file.txt'

# watcher = Watcher(watch_file)  # simple
watcher = Watcher(watch_file, custom_action, text='yes, changed')  # also call custom action function
watcher.watch()  # start the watch going

2
あなたは作ることができるwatch_file_cached_stampのリストに、ループのためにそれらを渡って反復します。ただし、実際には大量のファイルに
適切に拡張

これは、実行されるたびにアクションをトリガーしませんか?_cached_stampは0に設定され、os.stat(self.filename).st_mtimeと比較されます。_cached_stampは、コンストラクターでos.stat(self.filename).st_mtimeに設定する必要がありますか?
Seanonymous

1
call_func_on_change()の最初の実行時にトリガーされますがlook()、その後_cached_stamp更新されるため、の値がos.stat(self.filename).st_mtime. _cached_stamp変更されるまで、再度トリガーされることはありません。
-4Oh4

1
最初の実行で呼び出され_cached_stampたくない場合は、コンストラクタでの値を設定できcall_func_on_change()ます
4Oh4

ファイルの変更時に関数を呼び出すためにスクリプトを使用しました。私の関数はあなたのように引数をとりません。私はそれを機能させるために* args、** kwargsを削除する必要があると思いました(変更のある行のみを挿入しました)。self.call_func_on_change(self) def custom_action(): watcher = Watcher(watch_file, custom_action())しかし、これは機能しませんでした。最初の反復中にのみアクションが呼び出されました:ファイルが変更されましたはい、変更されましたファイルが変更されましたファイルが変更されましたファイルが変更されました* argsを保持し、それを呼び出したときに機能し始めました:watcher = Watcher(watch_file, custom_action)なぜだろうか?
zwornik

7

同様の質問に対する私の答えを確認しください。Pythonで同じループを試すことができます。このページは提案します:

import time

while 1:
    where = file.tell()
    line = file.readline()
    if not line:
        time.sleep(1)
        file.seek(where)
    else:
        print line, # already has newline

Pythonでファイルtail()a fileも参照してください。


sys.stdout.write(line)できます。ファイルが切り捨てられている場合、コードは機能しません。Pythonには組み込み関数file()があります。
jfs 2008年

私はあなたのコードの修正版を投稿しました。それがあなたのために働くなら、あなたはそれをあなたの答えに組み込むかもしれません。
jfs 2008年

7

私にとって最も簡単な解決策は、ウォッチドッグのツールwatchmedoを使用することです

https://pypi.python.org/pypi/watchdogから、ディレクトリ内のsqlファイルを検索し、必要に応じてそれらを実行するプロセスができました。

watchmedo shell-command \
--patterns="*.sql" \
--recursive \
--command='~/Desktop/load_files_into_mysql_database.sh' \
.

6

まあ、Pythonを使用しているので、ファイルを開いて、そこから行を読み続けることができます。

f = open('file.log')

読み込まれた行がない場合は、処理します。

line = f.readline()
if line:
    // Do what you want with the line

通話を続けても大丈夫なのに readlineEOFにを。この場合、空の文字列を返し続けるだけです。また、ログファイルに何かが追加されると、必要に応じて、停止したところから読み取りが続行されます。

イベントまたは特定のライブラリを使用するソリューションを探している場合は、質問でこれを指定してください。それ以外の場合は、このソリューションで十分だと思います。


6

以下は、同じトリックを実行するように見え、ファイル全体をインポートしないKenderのコードの簡略化されたバージョンです。

# Check file for new data.

import time

f = open(r'c:\temp\test.txt', 'r')

while True:

    line = f.readline()
    if not line:
        time.sleep(1)
        print 'Nothing New'
    else:
        print 'Call Function: ', line

6

これは、UNIXタイプで実行され、dict(file => time)を使用してファイルを変更するための単純なウォッチャーを追加する、Tim Goldanのスクリプトの別の変更です。

使用法:whateverName.py path_to_dir_to_watch

#!/usr/bin/env python

import os, sys, time

def files_to_timestamp(path):
    files = [os.path.join(path, f) for f in os.listdir(path)]
    return dict ([(f, os.path.getmtime(f)) for f in files])

if __name__ == "__main__":

    path_to_watch = sys.argv[1]
    print('Watching {}..'.format(path_to_watch))

    before = files_to_timestamp(path_to_watch)

    while 1:
        time.sleep (2)
        after = files_to_timestamp(path_to_watch)

        added = [f for f in after.keys() if not f in before.keys()]
        removed = [f for f in before.keys() if not f in after.keys()]
        modified = []

        for f in before.keys():
            if not f in removed:
                if os.path.getmtime(f) != before.get(f):
                    modified.append(f)

        if added: print('Added: {}'.format(', '.join(added)))
        if removed: print('Removed: {}'.format(', '.join(removed)))
        if modified: print('Modified: {}'.format(', '.join(modified)))

        before = after

python3をサポートするように更新
ronedg

4

Tim Goldenの記事Horst Gutmannが指摘)を見るとわかるように、WIN32は比較的複雑で、単一のファイルではなくディレクトリを監視します。

.NET Python実装であるIronPythonを検討することをお勧めします。IronPythonを使用すると、.NETのすべての機能を使用できます。

System.IO.FileSystemWatcher

シンプルなイベントインターフェイスで単一のファイルを処理します。


@Ciastoは、基本的なPythonインストールではなく、Iron Pythonを使用可能にする必要があるためです。
Jon Cage

1

これは、ファイルの変更をチェックする例です。それを行うための最良の方法ではないかもしれませんが、それは確かに短い方法です。

ソースに変更が加えられたときにアプリケーションを再起動するための便利なツール。pygameでプレイするときにこれを作成したので、ファイルを保存した直後にエフェクトが発生することがわかります。

pygameで使用する場合は、「while」ループの内容をゲームループ(更新など)に配置してください。そうしないと、アプリケーションが無限ループに陥り、ゲームが更新されません。

file_size_stored = os.stat('neuron.py').st_size

  while True:
    try:
      file_size_current = os.stat('neuron.py').st_size
      if file_size_stored != file_size_current:
        restart_program()
    except: 
      pass

あなたが私がウェブで見つけた再起動コードが欲しかった場合。ここにあります。(質問には関係ありませんが、役に立つかもしれません)

def restart_program(): #restart application
    python = sys.executable
    os.execl(python, python, * sys.argv)

電子にあなたがやりたいことをさせて楽しんでください。


.st_mtime代わりにを使用する.st_size方が信頼性が高く、これを行うための同様に短い方法のようですが、OPはポーリングを介してそれを実行したくないと示しています。
martineau

1
ACTIONS = {
  1 : "Created",
  2 : "Deleted",
  3 : "Updated",
  4 : "Renamed from something",
  5 : "Renamed to something"
}
FILE_LIST_DIRECTORY = 0x0001

class myThread (threading.Thread):
    def __init__(self, threadID, fileName, directory, origin):
        threading.Thread.__init__(self)
        self.threadID = threadID
        self.fileName = fileName
        self.daemon = True
        self.dir = directory
        self.originalFile = origin
    def run(self):
        startMonitor(self.fileName, self.dir, self.originalFile)

def startMonitor(fileMonitoring,dirPath,originalFile):
    hDir = win32file.CreateFile (
        dirPath,
        FILE_LIST_DIRECTORY,
        win32con.FILE_SHARE_READ | win32con.FILE_SHARE_WRITE,
        None,
        win32con.OPEN_EXISTING,
        win32con.FILE_FLAG_BACKUP_SEMANTICS,
        None
    )
    # Wait for new data and call ProcessNewData for each new chunk that's
    # written
    while 1:
        # Wait for a change to occur
        results = win32file.ReadDirectoryChangesW (
            hDir,
            1024,
            False,
            win32con.FILE_NOTIFY_CHANGE_LAST_WRITE,
            None,
            None
        )
        # For each change, check to see if it's updating the file we're
        # interested in
        for action, file_M in results:
            full_filename = os.path.join (dirPath, file_M)
            #print file, ACTIONS.get (action, "Unknown")
            if len(full_filename) == len(fileMonitoring) and action == 3:
                #copy to main file
                ...

1

以下は、毎秒1行以下で通常は大幅に少ない入力ファイルの監視に向けた例です。目的は、指定した出力ファイルに最後の行(最新の書き込み)を追加することです。これを自分のプロジェクトの1つからコピーして、関係のない行をすべて削除しました。不足している記号を入力するか変更する必要があります。

from PyQt5.QtCore import QFileSystemWatcher, QSettings, QThread
from ui_main_window import Ui_MainWindow   # Qt Creator gen'd 

class MainWindow(QMainWindow, Ui_MainWindow):
    def __init__(self, parent=None):
        QMainWindow.__init__(self, parent)
        Ui_MainWindow.__init__(self)
        self._fileWatcher = QFileSystemWatcher()
        self._fileWatcher.fileChanged.connect(self.fileChanged)

    def fileChanged(self, filepath):
        QThread.msleep(300)    # Reqd on some machines, give chance for write to complete
        # ^^ About to test this, may need more sophisticated solution
        with open(filepath) as file:
            lastLine = list(file)[-1]
        destPath = self._filemap[filepath]['dest file']
        with open(destPath, 'a') as out_file:               # a= append
            out_file.writelines([lastLine])

もちろん、包含するQMainWindowクラスは厳密には必要ありません。QFileSystemWatcherを単独で使用できます。



0

誰もfswatchを投稿していないようです。これは、クロスプラットフォームのファイルシステムウォッチャーです。インストールして実行し、プロンプトに従ってください。

私はそれをpythonおよびgolangプログラムで使用しましたが、うまくいきます。


0

関連する@ 4Oh4ソリューションは、監視するファイルのリストをスムーズに変更します。

import os
import sys
import time

class Watcher(object):
    running = True
    refresh_delay_secs = 1

    # Constructor
    def __init__(self, watch_files, call_func_on_change=None, *args, **kwargs):
        self._cached_stamp = 0
        self._cached_stamp_files = {}
        self.filenames = watch_files
        self.call_func_on_change = call_func_on_change
        self.args = args
        self.kwargs = kwargs

    # Look for changes
    def look(self):
        for file in self.filenames:
            stamp = os.stat(file).st_mtime
            if not file in self._cached_stamp_files:
                self._cached_stamp_files[file] = 0
            if stamp != self._cached_stamp_files[file]:
                self._cached_stamp_files[file] = stamp
                # File has changed, so do something...
                file_to_read = open(file, 'r')
                value = file_to_read.read()
                print("value from file", value)
                file_to_read.seek(0)
                if self.call_func_on_change is not None:
                    self.call_func_on_change(*self.args, **self.kwargs)

    # Keep watching in a loop
    def watch(self):
        while self.running:
            try:
                # Look for changes
                time.sleep(self.refresh_delay_secs)
                self.look()
            except KeyboardInterrupt:
                print('\nDone')
                break
            except FileNotFoundError:
                # Action on file not found
                pass
            except Exception as e:
                print(e)
                print('Unhandled error: %s' % sys.exc_info()[0])

# Call this function each time a change happens
def custom_action(text):
    print(text)
    # pass

watch_files = ['/Users/mexekanez/my_file.txt', '/Users/mexekanez/my_file1.txt']

# watcher = Watcher(watch_file)  # simple



if __name__ == "__main__":
    watcher = Watcher(watch_files, custom_action, text='yes, changed')  # also call custom action function
    watcher.watch()  # start the watch going

0

最良かつ最も簡単な解決策は、pygtailを使用することです:https ://pypi.python.org/pypi/pygtail

from pygtail import Pygtail
import sys

while True:
    for line in Pygtail("some.log"):
        sys.stdout.write(line)

-2

Windows固有の機能は知りません。ファイルのMD5ハッシュを毎秒/分/時間(必要な速さによる)で取得して、最後のハッシュと比較することもできます。異なる場合は、ファイルが変更されたことがわかり、最新の行が読み取られます。


-6

私はこのようなものを試してみます。

    try:
            f = open(filePath)
    except IOError:
            print "No such file: %s" % filePath
            raw_input("Press Enter to close window")
    try:
            lines = f.readlines()
            while True:
                    line = f.readline()
                    try:
                            if not line:
                                    time.sleep(1)
                            else:
                                    functionThatAnalisesTheLine(line)
                    except Exception, e:
                            # handle the exception somehow (for example, log the trace) and raise the same exception again
                            raw_input("Press Enter to close window")
                            raise e
    finally:
            f.close()

ループは、ファイルが最後に読み取られてから新しい行があるかどうかをチェックします。新しい行がある場合は、読み取られてfunctionThatAnalisesTheLine関数に渡されます。そうでない場合、スクリプトは1秒待機してからプロセスを再試行します。


4
-1:ファイルが数百MBになる可能性がある場合、ファイルを開いて行を読み取るのは良い考えではありません。何千ものファイルを監視したい場合は、ファイルごとに実行する必要があります。
ジョンケージ

1
本当に?変更のためにファイルを開いていますか?
Farsheed 2014
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.