1

我正在尝试编写一个脚本,该脚本在删除python3 -m http.server某个目录(_site)然后重新创建时重新启动进程。脚本如下。该函数中有一个 inotify 命令,waitdel它应该只阻塞直到目录被删除。当它被删除时,执行会继续进行一个简单的轮询等待,直到创建目录,然后服务器重新启动,最后我们又回到等待状态。

问题是,当_site被删除时, shell 脚本中永远不会退出,即使当我在运行此脚本的同一 bash 提示符下运行inotifywait完全相同的命令时也会退出,无论是 on还是.DELETEDELETE_SELF

我已经验证inotifywait运行了正确的命令,并且服务器进程没有阻止脚本的执行。那么,为什么它没有在脚本中退出?

#!/usr/bin/env bash
# serve.bash --- serve content, respawn server when _site/ is deleted

# bash strict mode
set -euo pipefail
IFS=$'\n\t'

PID=0
DIR="$PWD/_site"                # NO FOLLOWING SLASH OR BREAKS INOTIFYWAIT
PIDFILE="$PWD/.server.pid"

die () {
    echo $0: error: $@
    exit 2
}

say () {
    echo $0 \[$(date)\]: $@
}

serve () {
    cleanup
    old="$PWD"
    cd "$DIR" || die Could not cd to "$DIR"
    python3 -m http.server 8000 --bind 127.0.0.1 2>&1 &
    echo $! > "$PIDFILE"
    cd "$old"
}

waitdel () {
    while true; do
        say Set up watcher for "$DIR"...
        inotifywait -e delete_self "$DIR"
        say "$DIR" deleted, restarting server...

        # Wait&poll till the directory is recreated.
        while [ ! -e "$DIR" ]; do
            sleep 0.1
        done

        serve
    done
}

cleanup () {
    if [[ ! -e "$PIDFILE" ]]; then
        return
    fi
    sync
    PID="$(cat $PIDFILE)" && rm "$PIDFILE"
    say Kill pid="$PID"...
    [ "0" = "$PID" ] || kill -9 "$PID" \
        || die Failed to kill preexisting server on pid "$PID"
}


trap cleanup SIGINT SIGTERM EXIT

if [ -e "$PIDFILE" ]; then
    if pgrep -a python3 | grep http\\.server >/dev/null; then
        trap - SIGINT SIGTERM EXIT
        die Stale pidfile found at "$PIDFILE", a potentially orphaned \
            server might be running.  Please kill it before proceeding.
    else
        rm "$PIDFILE"               # remove stale pidfile when no server proc found
    fi
fi


serve

waitdel
4

1 回答 1

0

根据@oguz ismail 的建议,我尝试制作可以重现该问题的脚本的最小版本,这里是:

DIR="$PWD/_site"
mkdir -p $DIR
old="$PWD"
cd "$DIR" || die Could not cd to "$DIR"
python3 -m http.server 8000 --bind 127.0.0.1 2>&1 &      # (1)
cd "$old"
pid=$!
inotifywait -e delete_self "$DIR" &                      # (2)
sleep 1
rmdir $DIR
sleep 1
kill $pid
echo kill                                                # (3)

这里发生了什么:通过样板文件,在表达式(1)中,我们启动了一个 Python http.server 进程,其 CWD 为$DIR. 如果不是这样,即 CWD 是$(dirname $DIR),则 inotifywait成功终止。在(3)中我们清理了服务器进程。如果我们杀死 python 进程,inotifywait 就会终止,如果我们不这样做,它就不会。过程的输出

Setting up watches.
Watches established.
Serving HTTP on 127.0.0.1 port 8000 (http://127.0.0.1:8000/) ...
kill
/home/g/co/gkayaalp.com/www/_site/ DELETE_SELF 

表明 inotifywait 在 (3) 之后终止。

所以 inotifywait 挂起是因为$DIR忙(我猜是因为 inotify 与 inode 一起工作,python 进程挂在 inode 上,延迟了删除事件的传播)。解决此问题的一种快速方法是查看父目录。我waitdel这样修改:

waitdel () {
    while true; do
        say Set up watcher for "$DIR"...
        while inotifywait -qq -e delete "$DIR/.."; do
            if [ ! -e $DIR ]; then
                say "$DIR" deleted, restarting server...

                # Wait&poll till the directory is recreated.
                while [ ! -e "$DIR" ]; do
                    sleep 0.1
                done

                serve
            fi
        done
    done
}

现在它在哪里跟踪 DELETE 事件$DIR/..,并在每个上检查是否$DIR被删除。如果是,它会等待重新生成目录,然后运行serve它会杀死现有的 python 进程并生成一个新进程。

于 2020-05-22T09:31:36.733 回答