How to use inotifywait to watch a directory for creation of files of a specific extension

I have seen this answer.

You should consider using inotifywait, as an example:

inotifywait -m /path -e create -e moved_to |
    while read path action file; do
        echo "The file '$file' appeared in directory '$path' via '$action'"
        # do something with the file
    done

My question is that, the above script watches a directory for creation of files of any type, but how do I modify the inotifywait command to report only when a file of certain type/extension is created (or moved into the directory) – e.g. it should report when any .xml file is created.

WHAT I TRIED:

I have run the inotifywait --help command, and have read the command line options. It has --exclude <pattern> and --excludei <pattern> commands to EXCLUDE files of certain types (by using regEx), but I need a way to INCLUDE just the files of a certain type/extension.

Answers:

Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.

Method 1

how do I modify the inotifywait command to report only when a file of
certain type/extension is created

Please note that this is untested code since I don’t have access to inotify right now. But something akin to this ought to work:

inotifywait -m /path -e create -e moved_to |
    while read directory action file; do
        if [[ "$file" =~ .*xml$ ]]; then # Does the file end with .xml?
            echo "xml file" # If so, do your thing here!
        fi
    done

Method 2

Whilst the double-negative approach of the previous answer is a nice idea since (as TMG noted) it does indeed shift the job of filtering to inotifywait, it is not correct.

For example, if a file ends in as then it will not match [^j][^s]$ because the final letter s does not match [^s], therefore it will not be excluded.

In Boolean terms, if S is the statement:

“the final letter is s

and J is the statement:

“the penultimate letter is j

then the value of the --exclude parameter should semantically equate to not(J and S), which by De Morgan’s laws is not(J) or not(S).

Another potential problem is that in zsh, $path is a built-in variable representing the array equivalent of $PATH, so the while read path ... line will completely mess up $PATH and cause everything to become unexecutable from the shell.

Therefore the correct approach is:

inotifywait -m --exclude "[^j].$|[^s]$" /path -e create -e moved_to |
    while read dir action file; do
        echo "The file '$file' appeared in directory '$dir' via '$action'"
    done

Note the . which is needed after [^j] to ensure that the match is applied in the penultimate position, and also that the | character (representing the boolean OR mentioned above) should not be escaped here because --exclude takes POSIX extended regular expressions.

However, please see and upvote @ericcurtin’s answer which for newer versions of inotifywait is a far cleaner approach.

Method 3

--include and --includei are actual options, in the master version of the binaries at least:

https://github.com/inotify-tools/inotify-tools/tree/master

Method 4

Use a double negative:

inotifywait -m --exclude "[^j][^s]$" /path -e create -e moved_to |
    while read path action file; do
        echo "The file '$file' appeared in directory '$path' via '$action'"
    done

This will only include javascript files

Method 5

Starting with inotifywait version 3.20.1 you can use an --include option.

inotifywait example with --include

Here’s an example that uses --include to run a script when a wav file is added to a directory.

#!/bin/sh

# Usage
#
# Call the script with the directory you want to watch as an argument. e.g.:
# watchdir.sh /app/foo/
#
#
# Description:
# Uses inotifywait to look for new files in a directory and process them:
# inotifywait outputs data which is passed (piped) to a do for subcommands.
#
#
# Requirements:
# Requires inotifywait which is part of inotify-tools.
# e.g. yum install -y inotify-tools or apt-get install -y inotify-tools.
#
#
# See also:
# https://linux.die.net/man/1/inotifywait
# https://github.com/inotify-tools/inotify-tools/wiki#info
# (Might be interested in pyinotify too.)


echo "Watch $1 for file changes..."


# Be careful not to confuse the output of `inotifywait` with that of `echo`.
# e.g. missing a `` would break the pipe and write inotifywait's output, not
# the echo's.
inotifywait 
  $1 
  --monitor 
  -e create 
  --timefmt '%Y-%m-%dT%H:%M:%S' 
  --format '%T %w %f %e' 
  --include ".wav" 
| while read datetime dir filename event; do
  echo "Event: $datetime $dir$file $event"
  echo "Now, we could pass $datetime $dir $filename and $event to some other command."

  echo "Here's the extensionless filename:" ${filename%.*}
  echo "And the extension if needed:" ${filename##*.}

  python3 /app/phono.py --custom $dir$filename $dir${filename%.*}.txt /tmp/${filename%.*}
done


# --format:
#   %T  Replaced with the current Time in the format specified by the --timefmt option, which should be a format string suitable for passing to strftime(3).
#   %w  This will be replaced with the name of the Watched file on which an event occurred.
#   %f  When an event occurs within a directory, this will be replaced with the name of the File which caused the event to occur. Otherwise, this will be replaced with an empty string.
#   %e  Replaced with the Event(s) which occurred, comma-separated.
#   %Xe Replaced with the Event(s) which occurred, separated by whichever character is in the place of 'X'.
#
# There's no --include option, but there's --exclude, which can be used to the same effect as an include.


# test the script by creating a file in the watched directory, e.g.
# touch /app/foo/file1.wav # will trigger the script
# touch /app/foo/file1.txt # will not trigger the script

Installing (or compiling) inotify-tools

inotifywait is part of inotify-tools. You can install it via the usual way, e.g.:

apt-get install -y inotify-tools

or

yum install -y inotify-tools

But if only older versions are available from the repos, you may want to compile from source. If so:

cd /tmp/inotify-tools/
wget https://github.com/inotify-tools/inotify-tools/releases/download/3.20.2.2/inotify-tools-3.20.2.2.tar.gz
tar xzvf inotify-tools-3.20.2.2.tar.gz
cd inotify-tools-3.20.2.2
./configure --prefix=/usr --libdir=/lib64
make
make install

Method 6

In my case I was trying to filter the output of inotifywait using something like grep, in order to wait for the creation of a single file with a particular extension. The trouble with this approach is that grep will just hang.

For some reason a SIGPIPE signal is either not sent, or not processed by inotifywait. I found this question, which ran into the same issue I had, and process substitution can be used as a workaround:

grep -E .*iso -m1 < <(inotifywait -qm -e create .)

Process substitution does not work in certain shells (eg. sh), so this does not work in every case.


From the other answers given, I could not find the –include option. I will likely be experimenting with –exclude.


All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x