Restart process on file change in Linux
Yes, you can watch a directory via the inotify system using inotifywait
or inotifywatch
from the inotify-tools.
inotifywait
will exit upon detecting an event. Pass option -r
to watch directories recursively. Example: inotifywait -r mydirectory
.
You can also specify the event to watch for instead of watching all events. To wait only for file or directory content changes use option -e modify
.
Automatically restart server on file change
cargo watch
is actually a plugin for the Rust build tool cargo, but it can watch any files and also run shell commands:
cargo watch -w src/ -w index.html -s "./start_server.sh &"
The start_server.sh
script should contain something like this:
kill $(pidof simple-http-server) # kill any running instances to free up port
simple-http-server
because when a server is still running in the background, the new instance won't be able to access the port.
This will run the command specified with -s "…"
and will rerun it whenever any files or directories watched with -w
change.
How do you setup script RELOAD/RESTART upon file changes using bash?
My suggestion is to let docker start a wrapper script that simply starts the real script in the background.
Then in an infinite loop:
- using inotifywait the wrapper waits for the appropriate change
- then kills/stop/reload/... the child process
- starts a new one in the background again.
How do I write a bash script to restart a process if it dies?
Avoid PID-files, crons, or anything else that tries to evaluate processes that aren't their children.
There is a very good reason why in UNIX, you can ONLY wait on your children. Any method (ps parsing, pgrep, storing a PID, ...) that tries to work around that is flawed and has gaping holes in it. Just say no.
Instead you need the process that monitors your process to be the process' parent. What does this mean? It means only the process that starts your process can reliably wait for it to end. In bash, this is absolutely trivial.
until myserver; do
echo "Server 'myserver' crashed with exit code $?. Respawning.." >&2
sleep 1
done
The above piece of bash code runs myserver
in an until
loop. The first line starts myserver
and waits for it to end. When it ends, until
checks its exit status. If the exit status is 0
, it means it ended gracefully (which means you asked it to shut down somehow, and it did so successfully). In that case we don't want to restart it (we just asked it to shut down!). If the exit status is not 0
, until
will run the loop body, which emits an error message on STDERR and restarts the loop (back to line 1) after 1 second.
Why do we wait a second? Because if something's wrong with the startup sequence of myserver
and it crashes immediately, you'll have a very intensive loop of constant restarting and crashing on your hands. The sleep 1
takes away the strain from that.
Now all you need to do is start this bash script (asynchronously, probably), and it will monitor myserver
and restart it as necessary. If you want to start the monitor on boot (making the server "survive" reboots), you can schedule it in your user's cron(1) with an @reboot
rule. Open your cron rules with crontab
:
crontab -e
Then add a rule to start your monitor script:
@reboot /usr/local/bin/myservermonitor
Alternatively; look at inittab(5) and /etc/inittab. You can add a line in there to have myserver
start at a certain init level and be respawned automatically.
Edit.
Let me add some information on why not to use PID files. While they are very popular; they are also very flawed and there's no reason why you wouldn't just do it the correct way.
Consider this:
PID recycling (killing the wrong process):
/etc/init.d/foo start
: startfoo
, writefoo
's PID to/var/run/foo.pid
- A while later:
foo
dies somehow. - A while later: any random process that starts (call it
bar
) takes a random PID, imagine it takingfoo
's old PID. - You notice
foo
's gone:/etc/init.d/foo/restart
reads/var/run/foo.pid
, checks to see if it's still alive, findsbar
, thinks it'sfoo
, kills it, starts a newfoo
.
PID files go stale. You need over-complicated (or should I say, non-trivial) logic to check whether the PID file is stale, and any such logic is again vulnerable to
1.
.What if you don't even have write access or are in a read-only environment?
It's pointless overcomplication; see how simple my example above is. No need to complicate that, at all.
See also: Are PID-files still flawed when doing it 'right'?
By the way; even worse than PID files is parsing ps
! Don't ever do this.
ps
is very unportable. While you find it on almost every UNIX system; its arguments vary greatly if you want non-standard output. And standard output is ONLY for human consumption, not for scripted parsing!- Parsing
ps
leads to a LOT of false positives. Take theps aux | grep PID
example, and now imagine someone starting a process with a number somewhere as argument that happens to be the same as the PID you stared your daemon with! Imagine two people starting an X session and you grepping for X to kill yours. It's just all kinds of bad.
If you don't want to manage the process yourself; there are some perfectly good systems out there that will act as monitor for your processes. Look into runit, for example.
Terminating and restarting a process
Start the new version of main.py, and then exit the old version of main.py normally, by just returning from your main code branch or by calling sys.exit. - there's no reason to force kill the old version. There's no reason that both versions can't be running at the same time as long as the old just quits after starting the new one.
You could do something like this:
def force_restart():
os.system("python main.py")
sys.exit(0)
On Linux and Mac, it doesn't matter if you've move or delete the original version of main.py
after starting the old version of the app. Linux will deal with that by keeping the old file so long as some process, in this case the initial version of the program, is still referring to that file.
Don't restart service more than 1 time within 60 seconds inotify
- Remember the last time you restarted httpd with
lasttime=$(date +%s)
- If the next restart is triggered, sleep up to the specified time subtracted with the difference between the last time it was restarted and now
difftime=$(($(date +%s) - lasttime)); if (( difftime < 60 )); then sleep $(( 60 - difftime )); fi; lasttime=$( ... )
So smth like:
if [[.... ]] ; then # Does the file end with .py css html js
// delay up until 60 seconds from the last restart
if [ -n "${lasttime:-}" ]; then
difftime=$((60 - ($(date +%s) - lasttime)))
if ((difftime > 0)); then
sleep "$difftime"
fi
fi
lasttime=$(date +%s)
systemctl restart httpd # If so, do your thing here!
#touch /home/centos/log.txt
echo "test"
fi
Related Topics
Cdc_Acm: Failed to Set Dtr/Rts - Can Not Communicate with Usb Cdc Device
Using Multiple Layers of Quotes in Bash
Pyinstaller on 32-Bit Linux - Importerror: The 'six' Package Is Required
How to Launch a Job in a Shell Which Will Persist Even If The Shell Which Launches It Terminates
Linux/Unix Socket Self-Connection
Does Grub Switch to Protected Mode
How to Setup Cron Job on Amazon Linux Ami
Why Doesn't Time() from Time.H Have a Syscall to Sys_Time
Bash Script: Can Not Properly Handle Sigtstp
How to Suppress Warnings in Qt Creator
How to Execute The Vim Commands Through Shell Script
Shipping Gnu/Linux Firefox Plugin with Shared Libraries (For Installation with No Root Access)
In Bash, How to Expand Variables Twice in Double Quotes
Difference Between Different Ways of Running Shell Script
Find Files and Print Only Their Parent Directories