fswatch icon indicating copy to clipboard operation
fswatch copied to clipboard

Excclusion / Inclusion filters do not seem to work on Linux

Open AngmarLord opened this issue 4 years ago • 14 comments

fswatch version - 1.14.0

Environment

CentOs 8.0 GCC version - 8.3.1

Description

The exclude, include filters do not seem to work. Assuming a file extension of the type .abc, exclusion of all, followed by inclusion of .abc, as indicated in documentation, does not create any events at all.

I tried several formats -

fswatch -x --event Created -e ".*" -i "\\.ext$" /path

and

fswatch -x --event Created -e '.*\.*$' -i '.*\.abc$' /path

apart from other variations. In most cases, I do not receive any events, using -v, it seems using an exclusion filter like those described above stops fswatch from adding the /path to watch-list.

Gets stuck at - start_monitor: Adding path: /home/bob1

Do not get the message - add_watch: Added: /home/bob1

AngmarLord avatar Jun 26 '20 19:06 AngmarLord

This is happening to me as well on Ubuntu 20.04 server, fswatch version 1.14.0.

As soon as I add the -e ".*" nothing will be picked up by fswatch

fswatch -1 -e ".*" -i "\.ext\$" /path works in mac but not on linux.

jleider avatar Sep 24 '20 15:09 jleider

Also here. I have a Makefile using fswatch. Last successfully used some time last year. I subsequently upgraded to ubuntu 20 and it no longer works, with symptoms as above.

Ubuntu 20 has fswatch 1.14.0. Launchpad indicates that ubuntu 18 had 1.11.2.

I rebuilt 1.11.3 (last release of minor version 1.11) and installed into /opt, and that works fine.

Conclusion: either a regression in fswatch >1.11.3 or a problem in the Ubuntu 20 packaging.

mcarpenter avatar Feb 28 '21 14:02 mcarpenter

Conclusion: either a regression in fswatch >1.11.3 or a problem in the Ubuntu 20 packaging.

It's a regression, because it's also happening on Arch (fswatch 1.16.0-1).

xxxserxxx avatar Oct 12 '21 13:10 xxxserxxx

Bisection indicates commit b04d0edc1dfb21e08beeb594378176e1812e997a "Do not scan a directory tree if the directory name is excluded by filters"

This appears to affect inotify (Linux) but also kqueue and poll monitors. The change for all 3 is equivalent, in the recursive scan() function:

commit b04d0edc1dfb21e08beeb594378176e1812e997a (HEAD, refs/bisect/bad)
Author: Enrico M. Crisostomo <[email protected]>
Date:   Mon May 7 00:33:58 2018 +0200

    Do not scan a directory tree if the directory name is excluded by filters

diff --git a/libfswatch/src/libfswatch/c++/inotify_monitor.cpp b/libfswatch/src/libfswatch/c++/inotify_monitor.cpp
index 7b60092..aee31a2 100644
--- a/libfswatch/src/libfswatch/c++/inotify_monitor.cpp
+++ b/libfswatch/src/libfswatch/c++/inotify_monitor.cpp
@@ -165,7 +165,7 @@ namespace fsw
      */
     if (!is_dir && !accept_non_dirs) return;
     if (!is_dir && directory_only) return;
-    if (!is_dir && !accept_path(path)) return;
+    if (!accept_path(path)) return;
     if (!add_watch(path, fd_stat)) return;
     if (!recursive || !is_dir) return;
[snip]

Excluding everything (-e '.*') then excludes the top level directory (path), since this (changed) line returns before the watch can be added (next line).

I don't see a generic workaround at the user level.

Perhaps scan() should special-case the initial, top-level call? There is already a parameter accept_non_dirs that is true on the initial call to "to allow watching a file when first invoked on a node". Perhaps this could be renamed (eg top_level_node) and used for both purposes?

A possible downside to that is that if a user passes a directory on the command line that matches an exclude flag then it... won't be excluded. That might be surprising.

mcarpenter avatar Oct 16 '21 14:10 mcarpenter

@mcarpenter Because you specifically talk about exclusion filters, I want to be clear that inclusion filters also don't work; this isn't an issue only if an exclusion is specified. E.g., if all you want to watch for are .ics files, the following does not work:

$ mkdir fswatchtest
$ (sleep 5 ; touch fswatchtest/bar) &
$ fswatch --include '.*\.ics$'  --event Created --event IsFile fswatchtest
/home/user/fswatchtest/bar

The documentation is not clear on this point, but I am assuming that -i and -I imply excluding everything that doesn't match the include pattern. If -i needs -e to have effect, then the code @mcarpenter found prevents -i from working, and it'd be good to have that documented as it would be an uncommon behavior for POSIX tools that provide include/exclude functionality.

xxxserxxx avatar Oct 16 '21 21:10 xxxserxxx

@xxxserxxx wrote:

I want to be clear that inclusion filters also don't work

I think you are describing documented behavior (but this detail is not present in the manual page). Section 4.8.2 of the info page here says:

   * All paths are accepted _by default_, unless an exclusion filter
     says otherwise.

   * Inclusion filters may override any other exclusion filter.

   * The order in the definition of filters in the command line has no
     effect.

This explains why "exclude everything" (eg -e . or -e '.*') is a common pattern (see first two posts in this thread).

The change that I cited means that subsequent includes won't work unless the argument given on the command line (eg top-level directory) also matches an include flag. And often that's not the case, because we just want to look for events on *.ics files in some directory with an unrelated name like calendar_files/ or ./.

mcarpenter avatar Oct 18 '21 08:10 mcarpenter

I missed the documentation.

Just to summarize what I understand: I see that if you do an exclude on a specific file name, but then an include on a variation of that file name, exclude+include works. Where it fails is when the exclude is only a wildcard, such as '.*' because -- as you say -- it's excluding the path itself, right?

The inclusion rule, even if documented, seems odd and particularly useless; it's a flag that is useless by itself, right? Or is there a case when -i by itself isn't essentially unused?

xxxserxxx avatar Oct 18 '21 14:10 xxxserxxx

The man page certainly needs some help ("the first matching expression wins" is misleading).

if you do an exclude on a specific file name, but then an include on a variation of that file name, exclude+include works.

I agree (inclusion wins over exclusion).

Where it fails is when the exclude is only a wildcard, such as '.*' because -- as you say -- it's excluding the path itself, right?

Right. This is the special case where the path to be watched is (now) ignored (before any watches get added even) because it matches the exclusion pattern, but not the inclusion pattern.

The inclusion rule, even if documented, seems odd and particularly useless; it's a flag that is useless by itself, right?

Given the default is "include", I agree: I don't see a use case for -i/-I without -e/-E.

mcarpenter avatar Oct 18 '21 20:10 mcarpenter

Is there any update on this? Or any workaround to watch for files only for a particular extension on Linux?

(I guess one workaround could be to use fswatch **/*.ext, but that doesn't take into account newly created files)

dinvlad avatar Dec 21 '21 02:12 dinvlad

Hi, I was hit by the same bug as well on Ubuntu 20.04 LTS. This seems like a severe bug to me, that should be looked into.

robfordww avatar Feb 17 '22 14:02 robfordww

Still seeing this bug at least in fswatch 1.14.0. If monitoring a whole directory, directly or recursively, using exclude * and include .ext fails to detect anything because the search path (the directory) cannot match the include regex (.ext).

I'm not sure whether this is fixed in a later version, but apt package managers seem to only have up through 1.14.0.

wcbdata avatar Mar 09 '24 22:03 wcbdata