# include " wtr/watcher.hpp "
# include < iostream >
# include < string >
using namespace std ;
using namespace wtr ;
// The event type, and every field within it, has
// string conversions and stream operators. All
// kinds of strings -- Narrow, wide and weird ones.
// If we don't want particular formatting, we can
// json-serialize and show the event like this:
// some_stream << event
// Here, we'll apply our own formatting.
auto show (event e) {
cout << to<string>(e. effect_type ) + ' '
+ to<string>(e. path_type ) + ' '
+ to<string>(e. path_name )
+ (e. associated ? " -> " + to<string>(e. associated -> path_name ) : " " )
<< endl;
}
auto main () -> int {
// Watch the current directory asynchronously,
// calling the provided function on each event.
auto watcher = watch ( " . " , show);
// Do some work. (We'll just wait for a newline.)
getchar ();
// The watcher would close itself around here,
// though we can check and close it ourselves.
return watcher. close () ? 0 : 1 ;
} # Sigh
PLATFORM_EXTRAS= $( test " $( uname ) " = Darwin && echo ' -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk -framework CoreFoundation -framework CoreServices ' )
# Build
eval c++ -std=c++17 -Iinclude src/wtr/tiny_watcher/main.cpp -o watcher $PLATFORM_EXTRAS
# Run
./watcher #include "wtr/watcher-c.h"
#include <stdio.h>
void callback ( struct wtr_watcher_event event , void * _ctx ) {
printf (
"path name: %s, effect type: %d path type: %d, effect time: %lld, associated path name: %sn" ,
event . path_name ,
event . effect_type ,
event . path_type ,
event . effect_time ,
event . associated_path_name ? event . associated_path_name : ""
);
}
int main () {
void * watcher = wtr_watcher_open ( "." , callback , NULL );
getchar ();
return ! wtr_watcher_close ( watcher );
}pip install wtr-watcher from watcher import Watch
with Watch ( "." , print ):
input ()cargo add wtr-watcher tokio futures use futures :: StreamExt ;
use wtr_watcher :: Watch ;
# [ tokio :: main ( flavor = "current_thread" ) ]
async fn main ( ) -> Result < ( ) , Box < dyn std :: error :: Error > > {
let show = |e| async move { println ! ( "{e:?}" ) } ;
let events = Watch :: try_new ( "." ) ? ;
events . for_each ( show ) . await ;
Ok ( ( ) )
} import * as watcher from 'watcher' ;
var w = watcher . watch ( '.' , ( event ) => {
console . log ( event ) ;
} ) ;
process . stdin . on ( 'data' , ( ) => {
w . close ( ) ;
process . exit ( ) ;
} ) ;上記のそれぞれの出力は、形式に応じて、これになります。
modify file /home/e-dant/dev/watcher/.git/refs/heads/next.lock
rename file /home/e-dant/dev/watcher/.git/refs/heads/next.lock -> /home/e-dant/dev/watcher/.git/refs/heads/next
create file /home/e-dant/dev/watcher/.git/HEAD.lock
楽しむ!
ファイルシステムイベントウォッチャー
私は、ウォッチャーのランタイムを比較的シンプルで構成する1579ラインとAPIの実用的なものを保持しようとしています。
auto w = watch(path, [](event ev) { cout << ev; });wtr.watcher ~ウォッチャーは、図書館、プログラム、またはその両方として使用できます。ライブラリで何かを作成しようとしていない場合は、心配する必要はありません。私たちのものを使用するだけで、JSONとしてファイルシステムイベントを印刷するファイルシステムウォッチャーがあります。きちんとした。方法は次のとおりです。
# The main branch is the (latest) release branch.
git clone https://github.com/e-dant/watcher.git && cd watcher
# Via Nix
nix run | grep -oE ' cmake-is-tough '
# With the build script
tool/build --no-build-test --no-run && cd out/this/Release # Build the release version for the host platform.
./wtr.watcher | grep -oE ' needle-in-a-haystack/.+" ' # Use it, pipe it, whatever. (This is an .exe on Windows.)このプロジェクトでファイルシステム全体を視聴できます。ほとんどすべての場合、ほぼゼロの量のリソースを使用し、キャッシュを効率的に使用します。イベントを検出してユーザーに送信するオーバーヘッドは、測定されているファイルシステム操作よりも1桁少ないことをテストします。
このプロジェクトは、利用可能なすべての消毒剤に対するユニットテストを通じて実行します。このコードは、スレッド、メモリ、境界、タイプ、リソースセーフになるように懸命に努力します。言語に欠けていることは、テストで補おうとします。安全性の実際的な定義のために、このプロジェクトはおそらく適合します。
ウォッチャーは、C ++標準ライブラリに依存します。効率のために、Linux、Darwin、Windowsで可能な場合はOSを活用します。テストとデバッグには、スニッチと消毒剤を使用します。
ウォッチャーはほとんどどこでも実行可能です。唯一の要件はファイルシステムです。
重要な作品は、(ヘッダーのみの)ライブラリと(オプションの)CLIプログラムです。
include/wtr/watcher.hpp 。これを含めて、C ++プロジェクトでWatcherを使用してください。これをプロジェクトにコピーし、 #include "wtr/watcher.hpp" (または類似)として含めるだけで、この言語で起きて実行するのに十分です。いくつかの追加のドキュメントと高レベルのライブラリの内部は、イベントと監視ヘッダーにあります。watcher-c 。これを構築して、cまたは他の言語でFFIを介してウォッチャーを使用します。src/wtr/watcher/main.cpp 。これを構築して、コマンドラインからウォッチャーを使用します。出力は徹底的なJSONストリームです。src/wtr/tiny_watcher/main.cpp 。非常に最小限で、より人間が読みやすいCLIプログラム。このソースは、C ++の使用例とほぼ同じです。ディレクトリツリーは、以下のメモにあります。
ここでの2つの基本的なビルディングブロックは次のとおりです。
watch関数またはクラス(言語に応じて)eventオブジェクト(または言語に応じて、また同様に名前が付けられています) watch 、文字列のようなものであり、コールバックで、機能のようなものです。たとえば、キャラクター配列watch 、C ++で閉鎖がうまく機能します。
さまざまな言語の例は、クイックスタートで見つけることができます。 APIは、言語間で比較的一貫しています。
ウォッチャーは、あなたがそれを止めるか、それが回復不可能なエラーにヒットするまで喜んで見続けます。
eventオブジェクトは、ファイルシステムイベントに関する情報を(あなたが)与えられたコールバックに渡すために渡すためにwatchために使用されます。
eventオブジェクトには以下が含まれます。
path_name 、これはイベントへの絶対的なパスです。path_type 、パスのタイプ。 1つ:dirfilehard_linksym_linkwatcherothereffect_type 、「何が起こったのか」。 1つ:renamemodifycreatedestroyownerothereffect_time 、エポック以来のナノ秒でのイベントの時間。associated (イベント、C ++)またはassociated_path_name (他のすべての実装、単一のパス名):(JavaScriptの場合、ラクダケースを使用して、その言語のエコシステムと一致することに注意してください。)
watcherタイプは特別です。
このタイプのイベントには、ウォッチャーからのメッセージが含まれます。エラーメッセージまたは重要なステータスの更新を受信できます。
この形式は、一般的なポータブル形式でウォッチャーからの非同期メッセージをサポートするために選択されました。
最も重要な「ウォッチャー」イベントの2つは、最初の「ライブ」イベントと最終的な「ダイ」イベントです。
メッセージは、監視されているベースパスに加えられているように見えます。
たとえば、Watcher at /a/pathを開いた後、Watcherからこれらのメッセージを受信することができます。
s/self/live@/a/pathe/self/die@/a/pathメッセージは常にsから始まり、操作が成功したことを示し、 wの非致命的な警告を示し、 eを示し、致命的な誤差を示します。
重要なことに、ウォッチャーを閉じると、常にエラーが発生します
self/liveメッセージはまだ送信されていません。または、言い換えれば、ウォッチャーが完全に開始していない場合。この場合、ウォッチャーは完全に開閉した後すぐに閉じ、すべての呼び出しでエラーを報告します。最後のイベントは、常にウォッチャーからのdestroyイベントです。いくつかのイベントevについては、このように解析することができます:
ev.path_type == path_type::watcher && ev.effect_type == effect_type::destroy;ハッピーハッキング。
このプロジェクトは、ファイルシステムイベントを簡単に操作できるようにしようとしています。優れたツールは使いやすいと思います。このプロジェクトが人間工学に基づいていない場合は、問題を提出してください。
この段落を書く直前に、このコミットを準備しながら取られた出力のスナップショットです。
{
"1666393024210001000" : {
"path_name" : " /home/edant/dev/watcher/.git/logs/HEAD " ,
"effect_type" : " modify " ,
"path_type" : " file "
},
"1666393024210026000" : {
"path_name" : " /home/edant/dev/watcher/.git/logs/refs/heads/next " ,
"effect_type" : " modify " ,
"path_type" : " file "
},
"1666393024210032000" : {
"path_name" : " /home/edant/dev/watcher/.git/refs/heads/next.lock " ,
"effect_type" : " create " ,
"path_type" : " other "
}
}これはかなりクールです。
有能なプログラムがここにあります。
このプロジェクトは、次のようにアクセスできます。
tool/build :C ++ヘッダー/ライブラリ、CLI、テスト、ベンチマークターゲットを含むtool/cross :多くのプラットフォーム用にクロスコンパイルされたwatcher-c共有ライブラリとヘッダーを含むwatcher-cライブラリ(静的および共有)、CLI、テスト、ベンチマークターゲットを含むこちらのパッケージをご覧ください。
nix build # To just build
nix run # Build the default target, then run without arguments
nix run . -- / | jq # Build and run, watch the root directory, pipe it to jq
nix develop # Enter an isolated development shell with everything needed to explore this projectbazel build cli # Build, but don't run, the cli
bazel build hdr # Ditto, for the single-header
bazel run cli # Run the cli program without argumentstool/build
cd out/this/Release
# watches the current directory forever
./wtr.watcher
# watches some path for 10 seconds
./wtr.watcher ' your/favorite/path ' -s 10これにより、いくつかのプラットフォーム固有の処理、リリース、デバッグ、および消毒剤のバリアントの構築、いくつかのテストの実行が可能になります。
cmake -S . -B out
cmake --build out --config Release
cd out
# watches the current directory forever
./wtr.watcher
# watches some path for 10 seconds
./wtr.watcher ' your/favorite/path ' -s 10すべてのプラットフォーム上のウォッチャーは、ファイルまたはディレクトリ上のアクセス時間を変更するだけの変更イベントを意図的に無視します。
これらのイベントの有用性は疑わしいものでした。
それは良いよりも有害に思えた。 MicrosoftのC#Watcherのような他のウォッチャーは、デフォルトでそれらを無視します。一部のユーザーアプリケーションは、変更イベントに依存して、ファイルをいつリロードするかを知るために依存しています。
より良い、より完全なソリューションが存在し、これらのデフォルトは再び変更される可能性があります。
プロセスIDからのイベント、「この」プロセスからの速記、および関心のあるイベントソースの種類を指定する方法は、より完全なソリューションの優れた候補である方法を提供します。
私がこれを最初に書いたとき、私はC ++に満足していました。後でこのプロジェクトを実験としてRustで書き直しました。錆には利点と欠点があります。表現するのは少し安全でしたが、他のものは間違いなくそうではありませんでした。たとえば、カーネルからのさまざまなサイズの不透明なタイプでポインター数学を行う必要性は、錆で表現する方が安全ではありません。他のことはより安全ですが、このプロジェクトはそれらからあまり利益を得ていません。
錆は本当に使いやすさと表現に輝いています。それはそれを使用するのに十分な理由かもしれません。とりわけ、私たちは非同期の特徴と代数的なタイプを非常に良いために作業することができました。
定義上、このプロジェクトのコードの大部分を「ただ」安全にすることができる言語があるかどうかはわかりません。
このプロジェクトの内臓、アダプターはカーネルに話しかけます。彼らは、安全でない、悪性、警告が豊富なシステムレベルのインターフェイスを使用することになります。
パブリックAPIはわずか100行で、よくテストされており、よくテストされ、人間が検証できます。そこにはあまり起こりません。
アダプターをC ABIで露出させてFFIを作成することは価値があるかもしれません。ほとんどの言語はそれに接続できるはずです。
プラットフォームアダプターの安全性は、必然的にインターフェイスの各プラットフォームのドキュメントに依存します。すべてのシステムレベルのインターフェイスと同様に、正しい事前条件とそれらの条件が明確に定義されている限り、私たちは大丈夫です。
プラットフォーム固有の実装の中で、 FSEvents APIがDarwinで使用され、 ReadDirectoryChanges APIがWindowsで使用されます。 Linuxで最適なアダプターを選択するために、いくつかの追加作業があります。 fanotifyアダプターは、カーネルバージョンが5.9を超えている場合に使用され、含有プロセスにはルートプライバレージがあり、必要なシステム呼び出しが許可されています。必要な特権版とカーネルバージョンにもかかわらず、容器またはcgroupの内側にある場合、 fanotifyに関連付けられたシステム呼び出しは許可される場合があります。 inotifyアダプターは、それ以外の場合は使用されます。 Linuxの選択コードはこちらで見つけることができます。
アダプターの名前空間はインラインです。 (内部) detail::...::watch()関数が呼び出されると、 watch()関数の1つの(および1つの)プラットフォーム-Specifc実装に解決します。 1つのシンボル、多くのプラットフォーム、プラットフォームはインラインネームスペースです。
私たちのプラットフォームに依存しないアダプターであるThe warthogを引き出すと、効率がヒットします。このアダプターは、(ダーウィンではなく)BSDやソラリスなどの優れた代替品がないプラットフォームで使用されます( warthog kqueue倒すため)。
Watcherは、 warthogよりも優れた代替手段がない場合、まだ比較的効率的です。親指のルールとして、1万人以上の道を走るのは、 warthogと一緒に自分がst音を立てるかもしれません。
BSDのより良いカーネルAPIの目を開いたままにします。
ウォッチャーがイベントをコールバックに送信する準備ができたときに、通信する信頼できる方法はありません。
数千のパスでは、これには数ミリ秒かかる場合があります。数万のパスについては、数秒待つことを検討してください。
プラットフォーム固有の実装のいずれも、どの属性が変更されたかに関する情報を提供していません。これにより、この情報を自分で保存することに依存するイベントをサポートします。パスのマップをstat構造へのマップを保存し、属性の変更に拡張することは、重要ではないメモリのコミットメントです。
所有者と属性イベントは、これらのイベントを効率的にサポートする方法がわからないため、サポートされていません。
/procおよび/sysを含む特別なファイルシステムは、 inotify 、 fanotify 、またはThe warthogで監視することはできません。将来の作業には、カーネルが使用するためのEBPFプログラムの派遣が含まれる場合があります。これにより、これらの特別なファイルシステムの一部でイベントをmodifyために監視できます。
inotifyを使用すると、監視されたファイルの数が制限されます。
ヘッダーのみのライブラリと小さなウォッチャーの場合、C ++ 17以降は問題ありません。
いつかC ++ 20コルーチンを使用する場合があります。
$ tool/gen-event/dir &
$ tool/gen-event/file &
$ valgrind --tool=cachegrind wtr.watcher ~ -s 30I refs: 797,368,564
I1 misses: 6,807
LLi misses: 2,799
I1 miss rate: 0.00%
LLi miss rate: 0.00%
D refs: 338,544,669 (224,680,988 rd + 113,863,681 wr)
D1 misses: 35,331 ( 24,823 rd + 10,508 wr)
LLd misses: 11,884 ( 8,121 rd + 3,763 wr)
D1 miss rate: 0.0% ( 0.0% + 0.0% )
LLd miss rate: 0.0% ( 0.0% + 0.0% )
LL refs: 42,138 ( 31,630 rd + 10,508 wr)
LL misses: 14,683 ( 10,920 rd + 3,763 wr)
LL miss rate: 0.0% ( 0.0% + 0.0% )名前空間とシンボルはdevel/includeフォルダーのディレクトリに密接に従います。インラインネームスペースは、 -のディレクトリにあります。
たとえば、 wtr::watchファイルの内側にありますdevel/include/wtr/watcher-/watch.hpp 。 wtr::watcher::watchの名前空間watcher 、この大会によって匿名です。
詳細:function ::detail::wtr::watcher::adapter::watch()はdevel/include/detail/wtr/watcher/adapter/*/watch.hppファイルの1 * (および1つだけ!)内で定義されています。
devel/includeのすべてのヘッダーは、 include/wtr/watcher.hppに融合し、includeガードが上部に追加されます。含まれるガードは、リリースバージョンでは変更されません。将来的には、そうかもしれません。
watcher
├── src
│ └── wtr
│ ├── watcher
│ │ └── main.cpp
│ └── tiny_watcher
│ └── main.cpp
├── out
├── include
│ └── wtr
│ └── watcher.hpp
└── devel
├── src
│ └── wtr
└── include
├── wtr
│ ├── watcher.hpp
│ └── watcher-
│ ├── watch.hpp
│ └── event.hpp
└── detail
└── wtr
└── watcher
├── semabin.hpp
└── adapter
├── windows
│ └── watch.hpp
├── warthog
│ └── watch.hpp
├── linux
│ ├── watch.hpp
│ ├── sysres.hpp
│ ├── inotify
│ │ └── watch.hpp
│ └── fanotify
│ └── watch.hpp
└── darwin
└── watch.hpp
tool/treeを実行してこのツリーをローカルに表示できます。
https://github.com/notify-rs/notify :
lines of code : 2799
lines of tests : 475
lines of docs : 1071
implementation languages : rust
interface languages : rust
supported platforms : linux, windows, darwin, bsd
kernel apis : inotify, readdirectorychanges, fsevents, kqueue
non-blocking : yes
dependencies : none
tests : yes
static analysis : yes (borrow checked, memory and concurrency safe language)
https://github.com/e-dant/watcher :
lines of code : 1579
lines of tests : 881
lines of docs : 1977
implementation languages : cpp
interface languages : cpp, shells
supported platforms : linux, darwin, windows, bsd
kernel apis : inotify, fanotify, fsevents, readdirectorychanges
non-blocking : yes
dependencies : none
tests : yes
static analysis : yes
https://github.com/facebook/watchman.git :
lines of code : 37435
lines of tests : unknown
lines of docs : unknown
implementation languages : cpp, c
interface languages : cpp, js, java, python, ruby, rust, shells
supported platforms : linux, darwin, windows, maybe bsd
kernel apis : inotify, fsevents, readdirectorychanges
non-blocking : yes
dependencies : none
tests : yes (many)
static analysis : yes (all available)
https://github.com/p-ranav/fswatch :
lines of code : 245
lines of tests : 19
lines of docs : 114
implementation languages : cpp
interface languages : cpp, shells
supported platforms : linux, darwin, windows, bsd
kernel apis : inotify
non-blocking : maybe
dependencies : none
tests : some
static analysis : none
https://github.com/tywkeene/go-fsevents :
lines of code : 413
lines of tests : 401
lines of docs : 384
implementation languages : go
interface languages : go
supported platforms : linux
kernel apis : inotify
non-blocking : yes
dependencies : yes
tests : yes
static analysis : none (gc language)
https://github.com/radovskyb/watcher :
lines of code : 552
lines of tests : 767
lines of docs : 399
implementation languages : go
interface languages : go
supported platforms : linux, darwin, windows
kernel apis : none
non-blocking : no
dependencies : none
tests : yes
static analysis : none
https://github.com/parcel-bundler/watcher :
lines of code : 2862
lines of tests : 474
lines of docs : 791
implementation languages : cpp
interface languages : js
supported platforms : linux, darwin, windows, maybe bsd
kernel apis : fsevents, inotify, readdirectorychanges
non-blocking : yes
dependencies : none
tests : some (js bindings)
static analysis : none (interpreted language)
https://github.com/atom/watcher :
lines of code : 7789
lines of tests : 1864
lines of docs : 1334
implementation languages : cpp
interface languages : js
supported platforms : linux, darwin, windows, maybe bsd
kernel apis : inotify, fsevents, readdirectorychanges
non-blocking : yes
dependencies : none
tests : some (js bindings)
static analysis : none
https://github.com/paulmillr/chokidar :
lines of code : 1544
lines of tests : 1823
lines of docs : 1377
implementation languages : js
interface languages : js
supported platforms : linux, darwin, windows, bsd
kernel apis : fsevents
non-blocking : maybe
dependencies : yes
tests : yes (many)
static analysis : none (interpreted language)
https://github.com/Axosoft/nsfw :
lines of code : 2536
lines of tests : 1085
lines of docs : 148
implementation languages : cpp
interface languages : js
supported platforms : linux, darwin, windows, maybe bsd
kernel apis : fsevents
non-blocking : maybe
dependencies : yes (many)
tests : yes (js bindings)
static analysis : none
https://github.com/canton7/SyncTrayzor :
lines of code : 17102
lines of tests : 0
lines of docs : 2303
implementation languages : c #
interface languages : c #
supported platforms : windows
kernel apis : unknown
non-blocking : yes
dependencies : unknown
tests : none
static analysis : none (managed language)
https://github.com/g0t4/Rx-FileSystemWatcher :
lines of code : 360
lines of tests : 0
lines of docs : 46
implementation languages : c #
interface languages : c #
supported platforms : windows
kernel apis : unknown
non-blocking : yes
dependencies : unknown
tests : yes
static analysis : none (managed language)