# 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 ( ) ;
} ) ;Output dari masing -masing di atas adalah sesuatu ini, tergantung pada formatnya:
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
Menikmati!
Pengamat Acara Sistem File
Saya mencoba menjaga garis 1579 yang membentuk runtime pengamat relatif sederhana dan API praktis:
auto w = watch(path, [](event ev) { cout << ev; });wtr.watcher ~Watcher dapat digunakan sebagai perpustakaan, program, atau keduanya . Jika Anda tidak ingin membuat sesuatu dengan perpustakaan, jangan khawatir. Cukup gunakan milik kami dan Anda memiliki diri Anda sebagai pengamat sistem file yang mencetak acara sistem file sebagai JSON. Rapi. Begini caranya:
# 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.)Anda dapat menonton seluruh sistem file dengan proyek ini. Dalam hampir semua kasus, kami menggunakan sumber daya yang hampir nol dan memanfaatkan cache secara efisien. Kami secara teratur menguji bahwa overhead mendeteksi dan mengirim acara kepada pengguna adalah urutan besarnya kurang dari operasi sistem file yang diukur.
Kami menjalankan proyek ini melalui unit tes terhadap semua pembersih yang tersedia. Kode ini berusaha keras untuk menjadi utas, memori, batas, jenis, dan aman sumber daya. Apa yang kurang dari bahasa, kami mencoba menebus dengan pengujian. Untuk beberapa definisi praktis tentang keselamatan, proyek ini mungkin cocok.
Watcher tergantung pada perpustakaan standar C ++. Untuk efisiensi, kami memanfaatkan OS bila memungkinkan di Linux, Darwin dan Windows. Untuk pengujian dan debugging, kami menggunakan snitch dan pembersih.
Watcher dapat dijalankan hampir di mana saja. Satu -satunya persyaratan adalah sistem file.
Potongan-potongan penting adalah pustaka (hanya header) dan program CLI (opsional).
include/wtr/watcher.hpp . Sertakan ini untuk menggunakan Watcher dalam proyek C ++ Anda. Menyalin ini ke dalam proyek Anda, dan memasukkannya sebagai #include "wtr/watcher.hpp" (atau serupa) cukup untuk bangun dan berjalan dalam bahasa ini. Beberapa dokumentasi tambahan dan internal perpustakaan tingkat tinggi dapat ditemukan di acara dan tajuk menonton.watcher-c . Bangun ini untuk menggunakan Watcher dari C atau melalui FFI dalam bahasa lain.src/wtr/watcher/main.cpp . Bangun ini untuk menggunakan pengamat dari baris perintah. Outputnya adalah aliran JSON yang lengkap.src/wtr/tiny_watcher/main.cpp . Program CLI yang sangat minim, lebih dapat dibaca manusia. Sumber untuk ini hampir identik dengan contoh penggunaan untuk C ++.Pohon direktori ada dalam catatan di bawah ini.
Dua blok bangunan mendasar di sini adalah:
watch atau kelas (tergantung bahasa)event (atau dinamai serupa, sekali lagi tergantung pada bahasa) watch Take a Path, yang merupakan hal seperti string, dan panggilan balik, dengan adalah hal seperti fungsi. Misalnya, watch array karakter dan penutupan akan bekerja dengan baik di C ++.
Contoh untuk berbagai bahasa dapat ditemukan di awal yang cepat. API relatif konsisten di seluruh bahasa.
Watcher akan dengan senang hati terus menonton sampai Anda menghentikannya atau menyentuh kesalahan yang tidak dapat dipulihkan.
Objek event digunakan untuk memberikan informasi tentang acara sistem file ke panggilan balik yang diberikan (oleh Anda) untuk watch .
Objek event akan berisi:
path_name , yang merupakan jalur absolut ke acara tersebut.path_type , jenis jalur. Salah satu:dirfilehard_linksym_linkwatcherothereffect_type , "What Happened". Salah satu:renamemodifycreatedestroyownerothereffect_time , waktu acara di nanoseconds sejak zaman.associated (suatu peristiwa, C ++) atau associated_path_name (semua implementasi lainnya, nama jalur tunggal):(Perhatikan bahwa, untuk JavaScript, kami menggunakan kasus unta, untuk konsisten dengan ekosistem bahasa itu.)
Jenis watcher itu istimewa.
Acara dengan jenis ini akan mencakup pesan dari Watcher. Anda dapat menerima pesan kesalahan atau pembaruan status penting.
Format ini dipilih untuk mendukung pesan asinkron dari pengamat dalam format generik dan portabel.
Dua acara "Watcher" paling penting adalah acara "langsung" awal dan acara "Die" terakhir.
Pesan itu muncul di prepended ke jalur dasar yang ditonton.
Misalnya, setelah membuka Watcher AT /a/path , Anda dapat menerima pesan -pesan ini dari Watcher:
s/self/live@/a/pathe/self/die@/a/path Pesan selalu dimulai dengan salah satu s , menunjukkan operasi yang berhasil, w , menunjukkan peringatan non-fatal, atau e , menunjukkan kesalahan fatal.
Yang penting, menutup pengamat akan selalu menghasilkan kesalahan jika
self/live belum dikirim; Atau, dengan kata lain, jika pengamat belum sepenuhnya memulai. Dalam hal ini, pengamat akan segera ditutup setelah membuka sepenuhnya dan melaporkan kesalahan dalam semua panggilan untuk ditutup. Acara terakhir akan selalu menjadi acara destroy dari Watcher. Anda dapat menguraikannya seperti ini, untuk beberapa acara ev :
ev.path_type == path_type::watcher && ev.effect_type == effect_type::destroy;Selamat peretasan.
Proyek ini mencoba memudahkan Anda untuk bekerja dengan acara sistem file. Saya pikir alat yang bagus mudah digunakan. Jika proyek ini tidak ergonomis, ajukan masalah.
Berikut adalah snapshot dari output yang diambil saat menyiapkan komit ini, tepat sebelum menulis paragraf ini.
{
"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 "
}
}Yang cukup keren.
Program yang mampu ada di sini.
Proyek ini dapat diakses melalui:
tool/build : Termasuk target header/perpustakaan C ++, CLI, tes dan tolok ukurtool/cross : Termasuk perpustakaan dan header bersama watcher-c , yang dikompilasi silang untuk banyak platformwatcher-c (statis dan dibagikan), target CLI, tes, dan tolok ukurLihat paketnya di sini.
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 10Ini akan menangani beberapa platform khusus, membangun varian rilis, debug, dan pembersih, dan menjalankan beberapa tes.
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 10Pengamat di semua platform dengan sengaja mengabaikan peristiwa modifikasi yang hanya mengubah waktu Acess pada file atau direktori.
Kegunaan dari peristiwa itu dipertanyakan.
Tampaknya lebih berbahaya daripada baik. Pengamat lain, seperti Microsoft's C# Watcher, abaikan secara default. Beberapa aplikasi pengguna bergantung pada acara modifikasi untuk mengetahui kapan diri mereka sendiri memuat ulang file.
Solusi yang lebih baik, lebih lengkap ada, dan default ini mungkin berubah lagi.
Memberikan cara untuk mengabaikan peristiwa dari proses-ID, steno dari proses "ini", dan cara untuk menentukan jenis sumber acara apa yang kami minati adalah kandidat yang baik untuk solusi yang lebih lengkap.
Saya merasa nyaman dengan C ++ ketika saya pertama kali menulis ini. Saya kemudian menulis ulang proyek ini di Rust sebagai percobaan. Ada manfaat dan kelemahan yang berkarat. Beberapa hal sedikit lebih aman untuk diekspresikan, hal -hal lain jelas tidak. Perlunya melakukan matematika pointer pada beberapa jenis buram berukuran beragam dari kernel, misalnya, tidak lebih aman untuk diekspresikan dalam karat. Hal -hal lain lebih aman, tetapi proyek ini tidak banyak mendapat manfaat dari mereka.
Rust benar -benar bersinar dalam kegunaan dan ekspresi. Itu mungkin cukup alasan untuk menggunakannya. Antara lain, kita bisa bekerja dengan sifat -sifat async dan tipe aljabar untuk kebaikan yang luar biasa.
Saya tidak yakin apakah ada bahasa yang dapat "hanya" membuat sebagian besar kode dalam proyek ini aman menurut definisi.
Busts dari proyek ini, adaptor, berbicara dengan kernel. Mereka terikat untuk menggunakan antarmuka tingkat sistem yang tidak aman, tidak diketik, dan kaya akan peringatan.
API publik hanya sekitar 100 baris, diketik dengan baik, diuji dengan baik, dan dapat diverifikasi manusia. Tidak banyak yang terjadi di sana.
Membuat FFI dengan mengekspos adaptor dengan A C ABI mungkin bermanfaat. Sebagian besar bahasa harus dapat menghubungkannya.
Keamanan adaptor platform harus tergantung pada dokumentasi masing -masing platform untuk antarmuka mereka. Seperti semua antarmuka tingkat sistem, selama kami memastikan kondisi pra-dan-pos yang benar, dan kondisi tersebut terdefinisi dengan baik, kami harus baik-baik saja.
Di antara implementasi khusus platform, API FSEvents digunakan pada Darwin dan API ReadDirectoryChanges digunakan pada windows. Ada beberapa pekerjaan tambahan yang kami lakukan untuk memilih adaptor terbaik di Linux. Adaptor fanotify digunakan ketika versi kernel lebih besar dari 5,9, proses yang mengandung memiliki privelege root, dan panggilan sistem yang diperlukan diizinkan. Panggilan sistem yang terkait dengan fanotify dapat dilarang ketika berada di dalam wadah atau cgroup, meskipun versi hak istimewa dan kernel yang diperlukan. Adaptor inotify digunakan sebaliknya. Anda dapat menemukan kode pilihan untuk Linux di sini.
Namespaces untuk adaptor kami sejalan. Ketika fungsi (internal) detail::...::watch() dipanggil, itu diselesaikan menjadi satu (dan hanya satu) implementasi platform-specifc dari fungsi watch() . Salah satu simbol, banyak platform, di mana platform adalah inline namespace.
Efisiensi terpukul ketika kami mengeluarkan warthog , adaptor platform-independen kami. Adaptor ini digunakan pada platform yang tidak memiliki alternatif yang lebih baik, seperti (bukan Darwin) BSD dan Solaris (karena warthog mengalahkan kqueue ).
Watcher masih relatif efisien ketika tidak memiliki alternatif yang lebih baik daripada warthog . Sebagai aturan jempol, memindai lebih dari seratus ribu jalan dengan warthog mungkin gagap.
Saya akan membuka mata untuk API kernel yang lebih baik di BSD.
Tidak ada cara yang dapat diandalkan untuk berkomunikasi ketika seorang pengamat siap mengirim acara ke panggilan balik.
Untuk beberapa ribu jalan, ini mungkin memakan waktu beberapa milidetik. Untuk puluhan ribu jalan, pertimbangkan untuk menunggu beberapa detik.
Tidak ada implementasi khusus platform yang memberikan informasi tentang atribut apa yang diubah. Ini membuat mendukung acara -acara tersebut tergantung pada menyimpan informasi ini sendiri. Menyimpan peta jalur ke struktur stat , yang mencurahkannya pada perubahan atribut, adalah komitmen memori yang tidak signifikan.
Acara pemilik dan atribut tidak didukung karena saya tidak yakin bagaimana mendukung peristiwa itu efisien.
Sistem file khusus, termasuk /proc dan /sys , tidak dapat ditonton dengan inotify , fanotify atau warthog . Pekerjaan di masa depan mungkin melibatkan pengiriman program EBPF untuk digunakan kernel. Ini akan memungkinkan kami untuk memantau untuk modify acara pada beberapa sistem file khusus tersebut.
Jumlah file yang ditonton terbatas saat inotify digunakan.
Untuk perpustakaan header saja dan pengamat kecil, C ++ 17 ke atas akan baik-baik saja.
Kita mungkin menggunakan C ++ 20 coroutine suatu hari nanti.
$ 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% ) Namespaces dan simbol mengikuti direktori dalam folder devel/include . Namespaces inline ada dalam direktori dengan - imbuhan.
Misalnya, wtr::watch ada di dalam file devel/include/wtr/watcher-/watch.hpp . The Namespace watcher di wtr::watcher::watch adalah anonim oleh konvensi ini.
Lebih mendalam: Fungsi ::detail::wtr::watcher::adapter::watch() didefinisikan di dalam satu (dan hanya satu!) Dari file-file devel/include/detail/wtr/watcher/adapter/*/watch.hpp , di mana * diputuskan pada waktu kompilasi (tergantung pada sistem operasi host).
Semua header dalam devel/include digabungkan menjadi include/wtr/watcher.hpp dan inclube guard ditambahkan ke atas. Penjaga termasuk tidak berubah dengan versi rilis. Di masa depan, mungkin.
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
Anda dapat menjalankan
tool/treeuntuk melihat pohon ini secara lokal.
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)