简体   繁体   中英

Restart process on file change in Linux

Is there a simple solution (using common shell utils, via a util provided by most distributions, or some simple python/... script) to restart a process when some files change?

It would be nice to simply call sth like watch -cmd "./the_process -arg" deps/* .

Update: A simple shell script together with the proposed inotify-tools (nice:) fit my needs (works for commands w/o arguments):

#!/bin/sh
while true; do
  $@ &
  PID=$!
  inotifywait $1
  kill $PID
done

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 .

This is an improvement on the answer provided in the question. When one interrupts the script, the run process should be killed.

#!/bin/sh

sigint_handler()
{
  kill $PID
  exit
}

trap sigint_handler SIGINT

while true; do
  $@ &
  PID=$!
  inotifywait -e modify -e move -e create -e delete -e attrib -r `pwd`
  kill $PID
done

Check out iWatch :

Watch is a realtime filesystem monitoring program. It is a tool for detecting changes in filesystem and reporting it immediately.It uses a simple config file in XML format and is based on inotify, a file change notification system in the Linux kernel.

than, you could watch files easily:

iwatch /path/to/file -c 'run_you_script.sh'

I find that this suits the full scenario requested by the PO quite very well:

ls *.py | entr -r python my_main.py 

See also http://eradman.com/entrproject/ , although a bit oddly documented. Yes, you need to ls the file pattern you want matched, and pipe that into the entr executable. It will run your program and rerun it when any of the matched files change.

There's a perl script call lrrr (little restart runner, really) that I'm a contributor on. I use it daily at work.

You can install it with cpanm App::lrrr if you have a perl and cpanm installed, and then use it as follows:

lrrr -w dirs,or_files,to-watch your_cmd_here

The w flag marks off files or directories to watch. Currently, it kills the process you ran if a file is changed, but I'm gonna add a feature soon to toggle that.

Please let me know if there's anything to be added!

I use this "one liner" to restart long-running processes based on file changes

trap 'kill %1' 1 2 3 6; while : ; do YOUR_EXE & inotifywait -r YOUR_WATCHED_DIRECTORY -e create,delete,modify || break; kill %1; sleep 3; done

This will start the process, keep its output to the same console, watch for changes, if there is one, it will shut down the process, wait three seconds (for further within-same-second writes or process shutdown times), then do the above again.
ctrl-c & ssh-disconnect will be respected and the process will exit once you're done.

For legibility:

trap 'kill %1' 1 2 3 6
while :
do
    YOUR_EXE &
    inotifywait \
        -r YOUR_WATCHED_DIRECTORY \
        -e create,delete,modify \
    || break
    kill %1
    sleep 3
done

Eg for a package.json-ran project

"module"          : "./dist/server.mjs",
"scripts"         : {
    "build"  : "cd ./src && rollup -c ",
    "watch"  : "cd ./src && rollup -c -w",
    "start"  : "cd ./dist && node --trace-warnings --enable-source-maps ./server.mjs",
    "test"   : "trap 'kill %1' 1 2 3 6; while : ; do npm run start & inotifywait -r ./dist -e create,delete,modify || break; kill %1; sleep 3; done"
},
"dependencies"    : {

Here now you can run npm run watch (which compiles from src to dist) in one activity, npm run test (the server runner & restarter) in another, and as you edit ./src files the builder process will update ./dist and the server will restart for you to test.

I needed a solution for golang's go run command which spawns a subprocess. So combining the answers above and pidtree gave me this script.

#!/bin/bash

# go file to run
MAIN=cmd/example/main.go

# directories to recursively monitor
DIRS="cmd pkg"

# Based on pidtree from https://superuser.com/a/784102/524394
pidtree() {
  declare -A CHILDS
  while read P PP; do
    CHILDS[$PP]+=" $P"
  done < <(ps -e -o pid= -o ppid=)

  walk() {
    echo $1
    for i in ${CHILDS[$1]}; do
      walk $i
    done
  }

  for i in "$@"; do
    walk $i
  done
}

sigint_handler()
{
  kill $(pidtree $PID)
  exit
}

trap sigint_handler SIGINT

while true; do
  go run $MAIN &
  PID=$!
  inotifywait -e modify -e move -e create -e delete -e attrib -r $DIRS
  
  PIDS=$(pidtree $PID)
  kill $PIDS

  wait $PID
  sleep 1
done

-m switch of inotifywait tool

As no answer here address -m switch of inotifywait tool, I will share this: think parallel !

How I do this:

  • If I want to trig when a file is modified, I will use CLOSE-WRITE event.
  • Instead on while true I use -m switch of inotifywait command.
  • As many editors write on new file then rename file, I watch for directory instead and wait for an event with correct filename.
#!/bin/sh

cmdFile="$1"

tempdir=$(mktemp -d)
notif="$tempdir/notif"
mkfifo "$notif"

inotifywait -me close_write "${cmdFile%/*}" >"$notif" 2>&1 &
notpid=$!

exec 5<"$notif"
rm "$notif"
rmdir "$tempdir"

"$@" & cmdPid=$!

trap "kill $notpid \$cmdPid; exit" 0 1 2 3 6 9 15

while read dir evt file <&5;do
    case $file in
        ${cmdFile##*/} )
            date +"%a %d %b %T  file '$file' modified."
            kill $cmdPid
            "$@" & cmdPid=$!
            ;;
    esac
done

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM