8

I'm looking for a reliable way to detect renaming of files and get both old and new file names. This is what I have so far:

COUNTER=0; inotifywait -m --format '%f' -e moved_from,moved_to ./ | while read FILE do if [ $COUNTER -eq 0 ]; then FROM=$FILE; COUNTER=1; else TO=$FILE; COUNTER=0; echo "sed -i 's/\/$FROM)/\/$TO)/g' /home/a/b/c/post/*.md" sed -i 's/\/'$FROM')/\/'$TO')/g' /home/a/b/c/post/*.md fi done 

It works, but it assumes you will never move files into or out of the watched folder. It also assumes that events come in pairs, first moved_from, then moved_to. I don't know if this is always true (works so far).

I read inotify uses a cookie to link events. Is the cookie accessible somehow? Lacking the cookie, I thought about using timestamps to link events together. Any tips on getting FROM and TO in a more reliable way?

Full script gist.

1 Answer 1

7

I think your approach is correct, and tracking the cookie is a robust way of doing this. However, the only place in the source of inotify-tools (3.14) that cookie is referenced is in the header defining the struct to match the kernel API.

If you like living on the edge, this patch (issue #72) applies cleanly to 3.14 and adds a %c format specifier for the event cookie in hex:

--- libinotifytools/src/inotifytools.c.orig 2014-10-23 18:05:24.000000000 +0100 +++ libinotifytools/src/inotifytools.c 2014-10-23 18:15:47.000000000 +0100 @@ -1881,6 +1881,12 @@ continue; } + if ( ch1 == 'c' ) { + ind += snprintf( &out[ind], size-ind, "%x", event->cookie); + ++i; + continue; + } + if ( ch1 == 'e' ) { eventstr = inotifytools_event_to_str( event->mask ); strncpy( &out[ind], eventstr, size - ind ); 

This change modifies libinotifytools.so, not the inotifywait binary. To test before installation:

LD_PRELOAD=./libinotifytools/src/.libs/libinotifytools.so.0.4.1 \ inotifywait --format="%c %e %f" -m -e move /tmp/test Setting up watches. Watches established. 40ff8 MOVED_FROM b 40ff8 MOVED_TO a 

Assuming that MOVED_FROM always occurs before MOVED_TO (it does, see fsnotify_move(), and it's an ordered queue, though independent moves might get interleaved) in your script you cache the details when you see a MOVED_FROM line (perhaps in an associative array indexed by ID), and run your processing when you see a MOVED_TO with the matching half of the information.

declare -A cache inotifywait --format="%c %e %f" -m -e move /tmp/test | while read id event file; do if [ "$event" = "MOVED_FROM" ]; then cache[$id]=$file fi if [ "$event" = "MOVED_TO" ]; then if [ "${cache[$id]}" ]; then echo "processing ..." unset cache[$id] else echo "mismatch for $id" fi fi done 

(With three threads running to shuffle a pair of files each 10,000 times, I never saw a single out of order event, or event interleaving. It may depend on filesystem and other conditions of course.)

4
  • 1
    Beautiful. I never received such a detailed and useful answer before. Thank you so much!! I think the inclusion of your patch in the official version might facilitate the creation of tools that help keep consistency between text files (html, css, md, ...) and referred assets (jpg, gif, mp4, ...). Are you sending a pull request? :) Commented Oct 23, 2014 at 20:02
  • I hope this feature will make it into the official version, but I have not forked it, and I'm not sure when I will find the time to fork and update all the comments (doxygen). Commented Oct 24, 2014 at 17:09
  • Would be really cool to get this in upstream! Commented Jul 28, 2016 at 12:37
  • 1
    Patch now merged. Commented Jun 21, 2020 at 10:00

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.