2
\$\begingroup\$

I want to read a file real time with a while loop that is being fed by the stdout of a process (inotifywait). The output could be multiple lines, but I as soon as I've found name of a specific file in a line, I want to discard all the other lines. How can you do that without breaking the while loop? I think my current code will be hard to maintain.

What I want:

 #!/bin/bash

 FILENAME=config.txt

 while read -r OUTPUT_DATE OUTPUT_FILE OUTPUT_EVENT; do

     # check if file name is found on line
     # grep -q will give me an exit code of 0
     # if file is found 
     echo "$OUTPUT_FILE" | grep -q "^$FILENAME"

     if [ $? -eq 0 ]; then
         # perform an action(restart nginx, for example)!
         # and discard the other lines but do no break loop
     fi
 done < <(inotifywait -m -e close_write --timefmt '%Y%m%d%H%M' --format '%T %f %e' ./)

Depending on the texteditor, inotifywait can emit multiple lines output for a single modified file, like:

201710092031 config.txt CLOSE_WRITE,CLOSE <-- I was looking for config.txt so stop reading lines
201710092031 config.txt CLOSE_WRITE,CLOSE
201710092031 .config.txt.edited CLOSE_WRITE,CLOSE

My current workaround: (save date and time in hidden file and prevent update if multiple actions occur too fast after each other)

#!/bin/bash

FILENAME=config.txt
FILENAME_EDITED=~/ahn/.$FILENAME.edited

while read -r OUTPUT_DATE OUTPUT_FILE OUTPUT_EVENT  
do    
    echo $OUTPUT_FILE | grep -q "^$FILENAME"

    if [ $? -eq 0 ]; then
        # if the date + time of changed file is similar to
        # the date + time in hidden file, or if hidden file
        # does not exist, perform action and update date+time
        # in hidden file
        if ( [ -f "$FILENAME_EDITED" ] && [ $( cat "$FILENAME_EDITED" ) != "$OUTPUT_DATE" ] ) || [ ! -f "$FILENAME_EDITED" ]; then
            echo Restart process etc.
        fi
    fi
done < <(inotifywait -m -e close_write --timefmt '%Y%m%d%H%M' --format '%T %f %e' ./)
\$\endgroup\$
  • \$\begingroup\$ Why not just set a variable to the timestamp? \$\endgroup\$ – Barmar Oct 13 '17 at 19:56
  • \$\begingroup\$ @Barmar: thanks. If I would call the new variable $LATEST_TIMESTAMP, that shortens my second if clause from [ -f "$FILENAME_EDITED" ] && [ $( cat "$FILENAME_EDITED" ) != "$OUTPUT_DATE" ] ) || [ ! -f "$FILENAME_EDITED" ] to [ $LATEST_TIMESTAMP -ne $OUTPUT_DATE ] . Feel stupid for not having done that in the first place. I still wonder though if I could break out of this while loop without killing the inotifywait process. \$\endgroup\$ – axm__ Oct 14 '17 at 17:52
  • \$\begingroup\$ I don't think it's possible. When the loop ends, it closes the pipe that reads from inotifywait. When inotifywait tries to write to the pipe, it gets an error. \$\endgroup\$ – Barmar Oct 14 '17 at 23:28
  • \$\begingroup\$ You could run inotifywait in a background process, writing to a named pipe. Run another background process that has the pipe open for reading, but doesn't read from it, like sleep 100000 < ~/ahn/.pipe. Then you can redirect the input of your while loop to the pipe. \$\endgroup\$ – Barmar Oct 14 '17 at 23:30

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.