fsnotify یک کتابخانه اعلان فایل سیستم نوشته شده به زبان Go است که قادر به نظارت بر تغییرات فایل‌ها و دایرکتوری‌ها در سیستم فایل است و زمانی که تغییرات رخ دهد، برنامه را مطلع می‌سازد. این کتابخانه از عملکرد چند سکویی پشتیبانی می‌کند و می‌تواند بر روی سیستم‌عامل‌های مختلف از جمله لینوکس، macOS و ویندوز اجرا شود. fsnotify از مکانیسم‌های اعلان سیستم فایل سیستم‌عامل‌های مختلف استفاده می‌کند، مانند inotify در لینوکس، FSEvents در macOS و ReadDirectoryChangesW در ویندوز.

برای استفاده از این کتابخانه، نسخه 1.17 یا بالاتر Go مورد نیاز است؛ مستندات کامل را می‌توانید در https://pkg.go.dev/github.com/fsnotify/fsnotify پیدا کنید.

پشتیبانی از سیستم‌عامل‌های مختلف:

پشتیبانی سیستم‌عامل وضعیت
inotify لینوکس پشتیبانی
kqueue BSD، macOS پشتیبانی
ReadDirectoryChangesW ویندوز پشتیبانی
FEN illumos پشتیبانی
fanotify لینوکس 5.9+ هنوز پشتیبانی نمی‌شود
AHAFS AIX شاخه AIX؛ ویژگی آزمایشی به دلیل عدم وجود نگهدارندگان و محیط تست
FSEvents macOS نیاز به پشتیبانی از x/sys/unix
USN Journals ویندوز نیاز به پشتیبانی از x/sys/windows
Polling همه هنوز پشتیبانی نمی‌شود

تست این کتابخانه بر روی اندروید و سولاریس که باید شامل لینوکس و illumos باشد، هنوز انجام نشده است.

مثال‌های استفاده

موارد استفادهٔ fsnotify شامل اما محدود به موارد زیر است:

  1. همگام‌سازی فایل واقعی: fsnotify قادر به نظارت بر تغییرات در سیستم فایل در زمان واقعی است و بنابراین مناسب برای پیاده‌سازی همگام‌سازی فایل واقعی است. زمانی که تغییرات در فایل مبدأ رخ می‌دهد، تغییرات بلافاصله به فایل مقصد همگام‌سازی می‌شود و توسعه اطمینان از سازگاری فایل‌ها را فراهم می‌کند.
  2. ساخت خودکار: fsnotify قادر است تغییرات در کد منبع پروژه و فایل‌های وابستگی را نظارت کرده، در صورت رخداد تغییرات، دستورات ساخت را فعال کند و توسعهٔ ساخت خودکار را دستیابی می‌کند. این کار می‌تواند زمان و تلاش مربوط به ساخت دستی را صرفه‌جویی کرده و کارایی توسعه را بهبود بخشد.
  3. پشتیبان‌گیری فایل: fsnotify می‌تواند تغییرات در فایل‌ها یا دایرکتوری‌هایی که نیاز به پشتیبان‌گیری دارند را نظارت کرده و در صورت رخداد تغییرات، فوراً پشتیبان‌گیری را آغاز کند. این اطمینان از امنیت داده را فراهم می‌آورد و از از دست دادن داده به دلیل از دست رفتن یا آسیب دیدن فایل جلوگیری می‌کند.
  4. نظارت در زمان واقعی بر روی وقایع فایل خاطره: fsnotify قادر است به نظارت بر عملیات‌هایی مانند ایجاد، اصلاح و حذف فایل‌های خاطره بپردازد و در صورت رخداد تغییرات در فایل‌های خاطره، برنامه‌های نظارت بر خاطره را به طور موثر فعال کند و تغییرات محتوای خاطره را به زمان واقعی نظارت کند.
  5. نظارت بر امنیت سیستم فایل: fsnotify می‌تواند رویدادهای امنیتی در سیستم فایل مانند دسترسی، اصلاح و حذف فایل‌ها را نظارت کند. این امکان را فراهم می‌آورد تا امنیت سیستم فایل نظارت شود و به طور سریع رویدادهای ناامن شناسایی و ثبت شوند.

مثال استفاده

یک مثال پایه:

package main

import (
    "log"

    "github.com/fsnotify/fsnotify"
)

func main() {
    // Create a new watcher.
    watcher, err := fsnotify.NewWatcher()
    if err != nil {
        log.Fatal(err)
    }
    defer watcher.Close()

    // Start listening for events.
    go func() {
        for {
            select {
            case event, ok := <-watcher.Events:
                if !ok {
                    return
                }
                log.Println("Event:", event)
                if event.Has(fsnotify.Write) {
                    log.Println("Modified file:", event.Name)
                }
            case err, ok := <-watcher.Errors:
                if !ok {
                    return
                }
                log.Println("Error:", err)
            }
        }
    }()

    // Add the path that needs to be monitored.
    err = watcher.Add("/tmp")
    if err != nil {
        log.Fatal(err)
    }

    // Block the main goroutine.
    <-make(chan struct{})
}

مثال‌های بیشتر می‌توان در cmd/fsnotify پیدا کرد و با استفاده از دستور زیر اجرا شد:

% go run ./cmd/fsnotify

مستندات مفصل‌تر را می‌توانید در godoc پیدا کنید: https://pkg.go.dev/github.com/fsnotify/fsnotify

سوالات متداول

آیا هنوز نظارت می‌شود اگر فایل به یک دایرکتوری دیگر منتقل شود؟

خیر، مگر آنکه شما در حال نظارت بر مکانیت باشید که به آن منتقل شده است.

آیا زیرشاخه‌ها را نظارت می‌کند؟

خیر، شما باید برای هر دایرکتوری که می‌خواهید نظارت کنید (نظارت بازگشتی قرار داده شده است: #18) نظارت اضافه کنید.

آیا نیاز است همزمان بر روی کانال‌های خطا و رویداد در یک گوروتین نظارت شود؟

بله. شما می‌توانید از select برای خواندن از هر دو کانال در یک گوروتین استفاده کنید (شما نیاز به شروع جداگانه یک گوروتین برای هر کانال ندارید؛ برای مثال را ببینید).

چرا اعلان‌ها بر روی NFS، SMB، FUSE، /proc یا /sys کار نمی‌کنند؟

fsnotify نیازمند پشتیبانی از سیستم عامل زیرین برای عملکرد است. پروتکل‌های فعلی NFS و SMB پشتیبانی شبکه‌ای برای اعلان فایل فراهم نمی‌کنند و فایل‌سیستم‌های مجازی /proc و /sys نیز پشتیبانی نمی‌کنند.

این موضوع با استفاده از یک ناظر اندازی‌گیری‌ای (پیشنهاد #9) قابل اصلاح است، اما هنوز پیاده‌سازی نشده است.

چرا من دارم رویدادهای Chmod بسیاری دریافت می‌کنم؟

برخی برنامه‌ها ممکن است تعداد زیادی تغییر ویژگی بوجود آورند، مانند Spotlight در macOS، برنامه‌های آنتی‌ویروس، برنامه‌های پشتیبان‌گیری و برخی برنامه‌های دیگر شناخته‌شده. به طور کلی، نادیده گرفتن رویدادهای Chmod معمولاً بهترین روش است زیرا اغلب بی‌فایده هستند و ممکن است مشکلاتی ایجاد کنند.

اندیسه کردن Spotlight در macOS ممکن است رویدادهای چندگانه‌ای را ایجاد کند (مشاهده #15). یک راه‌حل موقت این است که پوشه‌های خود را به تنظیمات حریم خصوصی Spotlight اضافه کنید تا زمانی که ما یک پیاده‌سازی Native FSEvents داشته باشیم (مشاهده #11).

نظارت بر فایل‌ها به خوبی کار نمی‌کند

به طور کلی، نهایتاً توصیه نمی‌شود که به جای دایرکتوری‌ها، فایل‌های فردی را نظارت کنید، زیرا بسیاری از برنامه‌ها، به ویژه ویرایشگرها، معمولاً فایل‌ها را به صورت اتمیکی به‌روزرسانی می‌کنند: به یک فایل موقتی می‌نویسند و سپس آن را به مکان مقصد منتقل می‌کنند، فایل اصلی را بازنویسی می‌کنند (یا شکل وارونه‌ای از آن). نظارت بر فایل اصلی در حال حاضر از بین رفته است زیرا فایل دیگر وجود ندارد.

بنابراین، قطع برق یا یک کرش به نتیجه یک فایل نیمه‌نوشته منتهی نمی‌شود.

نظارت بر پوشه و استفاده از Event.Name برای فیلتر کردن فایل‌هایی که علاقه‌مند نیستید، بهتر است. یک مثال در cmd/fsnotify/file.go وجود دارد.

یادداشت برای پلتفرم‌های خاص

لینوکس

وقتی یک فایل پاک شود، رویداد REMOVE صادر نخواهد شد تا زمانی که همه توصیف‌گرهای فایل بسته شود؛ در این زمان، یک رویداد CHMOD صادر می‌شود:

fp := os.Open("file")
os.Remove("file")        // CHMOD
fp.Close()               // REMOVE

این رویدادی است که توسط inotify ارسال می‌شود، بنابراین نباید تغییرات زیادی در آن صورت گیرد.

متغیر sysctl fs.inotify.max_user_watches حداکثر تعداد نظارت‌های هر کاربر را مشخص می‌کند، و fs.inotify.max_user_instances حداکثر تعداد نمونه inotify را برای هر کاربر مشخص می‌کند. هر نظارت کننده ایجاد شده یک "نمونه" است، و هر مسیری که اضافه کنید، "نظارت" است.

اینها نیز می‌توانند در /proc، با مسیر‌های /proc/sys/fs/inotify/max_user_watches و /proc/sys/fs/inotify/max_user_instances یافت شوند.

برای افزایش آنها، می‌توانید از sysctl استفاده کنید یا مقادیر را به فایل proc بنویسید:

sysctl fs.inotify.max_user_watches=124983
sysctl fs.inotify.max_user_instances=128

برای اینکه تغییرات پس از راه‌اندازی مجدد اثر کنند، ویرایش /etc/sysctl.conf یا /usr/lib/sysctl.d/50-default.conf را انجام دهید (جزئیات ممکن است برای هر توزیع لینوکس متفاوت باشد، لطفاً به مستندات توزیع خود مراجعه کنید):

fs.inotify.max_user_watches=124983
fs.inotify.max_user_instances=128

رسیدن به حد ممکن باعث ایجاد خطاهای "فضای کافی برای دستگاه موجود نیست" یا "تعداد زیادی فایل باز است" خواهد شد.

kqueue (macOS، تمام سیستم‌های BSD)

kqueue نیازمند باز کردن یک توصیف‌گر فایل برای هر فایل مشاهده شده است؛ بنابراین، اگر شما یک پوشه حاوی پنج فایل را مشاهده می‌کنید، شش توصیف‌گر فایل خواهید داشت. در این پلتفرم‌ها، شما به سرعت به حداکثر "تعداد فایل‌های باز" سیستم خواهید رسید.

شما می‌توانید از متغیرهای sysctl kern.maxfiles و kern.maxfilesperproc برای کنترل حداکثر تعداد فایل‌های باز استفاده کنید.