bash -- execute command on file change; doubling issue + how to skip loop until command completes - linux

I'm a bash noob, and I am trying to set up a sort of "hot reload" functionality for a project I'm working on using inotifywait. Ubuntu 20.04 if that matters.
Here is what I hoped would have worked:
inotifywait -m -r ../.. -e modify,create,delete |
while read line; do
custom_command
done
I'm having two problems:
Issue #1 is that custom_command takes some time to work, and so if I make more changes to the directory in the meantime, custom command appears to "queue up" custom_command, where really I just want it to keep the most recent one and drop the others.
Issue #2 is that I'm getting some sort of "double output." So for example if I bash auto-exec.sh and auto-exec.sh looks like this:
inotifywait -m -r . -q -e modify,create,delete
Then each time a change registers, I get this as output (not a mistake that it's doubled -- I get two identical lines each time there is a modification):
./ MODIFY auto-exec-testfile.txt
./ MODIFY auto-exec-testfile.txt
I should note I've tried making changes both with Visual Code Studio and gedit, with the same results.
If I modify the bash file like so:
inotifywait -m -r . -q -e modify,create,delete |
while read line; do
echo "$line"
echo "..."
done
I get the following output each time there is a change:
./ MODIFY auto-exec-testfile.txt
...
./ MODIFY auto-exec-testfile.txt
...
If I modify bash_test.sh to the following:
inotifywait -m -r . -q -e modify,create,delete |
while read line; do
echo "help me..."
done
Then I get the following each time a change is made:
help me...
help me...
What happened to the the ./ MODIFY ... line?? Presumably there's something I don't understand about bash, stdout or similar /related concepts here?
And finally, if I change the .sh file to the following:
inotifywait -m -r . -q -q -e modify,create,delete |
while read _; do
echo "help me..."
done
Then I get no output at all. This one I think I understand, because the -q -q means that inotifywait is in "super silent" mode, so there is no log and therefore nothing to trigger the while.
What I'd love to do is just trigger the code once when something changes, and drop all but the most recent execution. I'm not sure doing this using a while is entirely necessary, but I tried inotifywait -m -r . -q -q -e modify,create,delete | echo "help me..", and the script printed "help me..." once at startup, then exited on modification.
Assistance very much appreciated.
EDIT - 20201-Mar-23
I removed -m and create from the inotifywait line, and it appears to work as expected, except that it doesn't stay "up" in monitor mode. So this at least only gives me one entry from inotifywait:
notifywait -r .. -q -e modify,delete |
while read line1; do
echo ${line1}
done
Related:
inotifywait - pause monitoring while executing command
https://unix.stackexchange.com/questions/140679/using-inotify-to-monitor-a-directory-but-not-working-100
inotifywait not performing the while loop in bash script

while inotifywait -e close_write,delete .; do
pkill custom_command
custom_command&
done

Related

inotifywait not piping output to console

I have the following shell script running a inotifywait command. I want to print the output echo to the console upon every modify event.
The script:
#!/bin/sh
while inotifywait -e modify -r -m ./ --exclude '\.sh$'; do
echo test
done
When I change one file in the specified directory, i get the standard output from inotifywait:
Setting up watches. Beware: since -r was given, this may take a while!
Watches established.
./postgres/ MODIFY postgres_test.go
./postgres/ MODIFY postgres_test.go
I have two questions:
Why is the modified event registered twice? I only updated the file once.
Why is "test" not being printed to the console in which I'm running the script?
I had a similar issue. I resolved the second part by restructuring my while:
inotifywait -e modify -r -m ./ --exclude '\.sh$' |
while read E; do
echo "----------------hello $E"
done

Execute commands in specific location and depending on answer of previous command

I am currently working on a Text-to-speech project and I need to write bash script which will, when it is called, execute two commands. If the first command returns the proper answer (if returns an answer at all), the second command will be called and executed.
My question is, how can I write a script, that executes shell commands in a specific certain file system location?
For example, I need to be in the directory /opt/text/example and execute this command:
sudo ./bin/sample_read -I ../languages/ -I ../languages -v dave -T 2 \
-i /opt/text/example.txt -F 22 -O embedded-pro -o out_file.pcm
and then to wait for the answer, then (if it is good) execute the second command.
The second command is
aplay -f S16_LE -r 22050 -c 1 out_file.pcm
This should help:
pushd /path/to/directory
my_var=$(command1)
if [ "$my_var" == "expected_result" ]; then
command2
fi
popd
You basically run command1 and store its output in my_var. Then you compare the content of $my_var with whatever you're expecting.
Also pushd <path>/popd allow you to move to a directory and back.

creating bash script to automate task for analyzing multiple files

I don't have a lot of experience with scripting.
I have a directory that contains, among many other files, a set of *.phylip files I need to analyze with a program. I would like to automate this task. I think a loop bash shell script would be appropriate, although I could be wrong.
If I was to perform the analysis manually on one .phylip file, I would use the following command in terminal:
./raxmlHPC-SSE3 -m GTRCAT -y -s uce-5.phylip --print-identical-sequences -p 12345 -n uce-5_result
For the bash shell script, I think it would be close to:
#!/bin/sh
for i in $( ls ); do
./raxmlHPC-SSE3 -m GTRCAT -y -s uce-5.phylip --print-identical-sequences -p 12345 -n test_5 $i
done
The issue I'm aware of, but don't know how to fix, is the -s option, which specifies the input phylip file. Any suggestions on how to modify the script to do what I need done?
Try the below code:
#!/bin/bash
for i in *.phylip
do
./raxmlHPC-SSE3 -m GTRCAT -y -s "$i" --print-identical-sequences -p 12345 -n ${i%.phylip}_result
done
-s option will be passed $i which has the file name with .phylip extension in the current directory.
${i%.phylip}_result replaces the .phylip extension with _result which i guess is what you expect. (Ref: Parameter Substitution)

Using inotify in a script to monitor a directory

I have written a bash script to monitor a particular directory "/root/secondfolder/" the script is as follows:
#!/bin/sh
while inotifywait -mr -e close_write "/root/secondfolder/"
do
echo "close_write"
done
When I create a file called "fourth.txt" in "/root/secondfolder/" and write stuff to it, save and close it, it outputs the following but it does not echo "close_write":
/root/secondfolder/ CLOSE_WRITE,CLOSE fourth.txt
can someone point me in the right direction?
You are not far away from solution. If you want to use inotifywait in your while statement you should not use -m option. With this option inotifywait never end because it's the monitor option. So you never go into the while.
This should work :
#!/bin/sh
while inotifywait -r -e close_write "/root/secondfolder/"
do
echo "close_write"
done
It turns out all I had to do was pipe the command into a while loop:
!/bin/sh
inotifywait -mqr -e close_write "/root/secondfolder/" | while read line
do
echo "close_write"
done

Processing data with inotify-tools as a daemon

I have a bash script that processes some data using inotify-tools to know when certain events took place on the filesystem. It works fine if run in the bash console, but when I try to run it as a daemon it fails. I think the reason is the fact that all the output from the inotifywait command call goes to a file, thus, the part after | while doesn't get called anymore. How can I fix that? Here is my script.
#!/bin/bash
inotifywait -d -r \
-o /dev/null \
-e close_write \
--exclude "^[\.+]|cgi-bin|recycle_bin" \
--format "%w:%&e:%f" \
$1|
while IFS=':' read directory event file
do
#doing my thing
done
So, -d tells inotifywait to run as daemon, -r to do it recursively and -o is the file in which to save the output. In my case the file is /dev/null because I don't really need the output except for processing the part after the command (| while...)
You don't want to run inotify-wait as a daemon in this case, because you want to continue process output from the command. You want to replace the -d command line option with -m, which tells inotifywait to keep monitoring the files and continue printing to stdout:
-m, --monitor
Instead of exiting after receiving a single event, execute
indefinitely. The default behaviour is to exit after the
first event occurs.
If you want things running in the background, you'll need to background the entire script.
Here's a solution using nohup: (Note in my testing, if I specified the -o the while loop didn't seem to be evaluated)
nohup inotifywait -m -r \
-e close_write \
--exclude "^[\.+]|cgi-bin|recycle_bin" \
--format "%w:%&e:%f" \
$1 |
while IFS=':' read directory event file
do
#doing my thing
done >> /some/path/to/log 2>&1 &

Resources