即使使用PID文件

时间:2017-01-18 11:47:58

标签: php bash shell pid inotify

我编写了我的第一个bash脚本,它正在使用函数“inotify”检查文件夹中的更改并启动一些操作。整个过程由nohup作为后台进程运行。

该文件夹是多个数据记录器的目标,它们通过ftp将zip文件中的文件推送到不同的子文件夹中。 bash脚本解压缩文件并在之后启动一个php脚本,它正在处理zip文件的内容。

我的问题:有时bash脚本会给我以下错误:

- No zipfiles found.
- unzip:  cannot find zipfile...

这不应该发生,因为文件存在,我可以在终端中运行相同的命令而不会出错。之前我遇到过同样的问题,当我多次重复运行脚本时,我猜这是以某种方式导致问题。

我尝试使用PID文件管理问题,该文件位于我的家庭目录中。出于某种原因,它仍然运行bash脚本的两个实例。如果我尝试运行另一个实例,它会显示警告“正在运行的进程”(请参阅​​程序代码)。当我手动终止第二个实例的进程(kill $$)时,它会在一段时间后重新启动,并且有两个进程正在运行。

#!/bin/bash

PIDFILE=/home/PIDs/myscript.pid

if [ -f $PIDFILE ]
then
  PID=$(cat $PIDFILE)
  ps -p $PID > /dev/null 2>&1
  if [ $? -eq 0 ]
  then
    echo "Process already running"
    exit 1
  else
    ## Process not found assume not running
    echo $$ > $PIDFILE
    if [ $? -ne 0 ]
    then
      echo "Could not create PID file"
      exit 1
    fi
  fi
else
  echo $$ > $PIDFILE
  if [ $? -ne 0 ]
  then
    echo "Could not create PID file"
    exit 1
  fi
fi

while true;
do inotifywait -q -r -e move -e create --format %w%f /var/somefolder | while read FILE

do
    dir=$(dirname $FILE)
    filename=${FILE:$((${#dir}+1))}

    if [[ "$filename" == *.zip ]];
           then
                unzip $FILE
                php somephpscript $dir 
    fi
done
done

ps -ef的输出看起来像这样:

UID  PID  PPID C STIME TTY   TIME     CMD

root 1439 1433 0 11:19 pts/0 00:00:00 /bin/bash /.../my_script 
root 3488 1439 0 15:10 pts/0 00:00:00 /bin/bash /.../my_script 

如您所见,第二个实例Parent-PID是脚本本身

编辑:我根据Fred的建议更改了bash脚本。源代码现在看起来像这样:

#!/bin/bash

PIDFILE=/home/PIDs/myscript.pid

if [ -f $PIDFILE ]
then
  PID=$(cat $PIDFILE)
  ps -p $PID > /dev/null 2>&1
  if [ $? -eq 0 ]
  then
    echo "Process already running"
    exit 1
  else
    ## Process not found assume not running
    echo $$ > $PIDFILE
    if [ $? -ne 0 ]
    then
      echo "Could not create PID file"
      exit 1
    fi
  fi
else
  echo $$ > $PIDFILE
  if [ $? -ne 0 ]
  then
    echo "Could not create PID file"
    exit 1
  fi
fi

while read -r FILE
do
    dir=$(dirname $FILE)
    filename=${FILE:$((${#dir}+1))}

    if [[ "$filename" == *.zip ]];
           then
                unzip $FILE
                php somephpscript $dir 
    fi


done < <(inotifywait -q -m -r -e move -e create --format %w%f /var/somefolder)

ps -ef的输出仍显示两个实例:

UID       PID   PPID  C STIME TTY      TIME     CMD
root      7550  7416  0 15:59 pts/0    00:00:00 /bin/bash /.../my_script
root      7553  7550  0 15:59 pts/0    00:00:00 /bin/bash /.../my_script
root      7554  7553  0 15:59 pts/0    00:00:00 inotifywait -q -m -r -e move -e create --format %w%f /var/somefolder

1 个答案:

答案 0 :(得分:0)

您在ps输出中看到两行,并假设这意味着您的脚本已启动两次,但事实并非如此。

您将inotifywait传递到while循环(这是正常的)。你可能没有意识到的是,通过这样做,你可以让Bash创建一个子shell来执行while循环。该子shell不是整个脚本的完整副本。

如果你杀死那个子shell,由于while true循环,它会立即重新创建。请注意inotifywait有一个--monitor选项;我没有详细研究过您的脚本,但也许您可以通过使用它来取消外部while循环。

还有另一种编写循环的方法,它不会消除子shell但具有其他优点。尝试类似:

while IFS= read -r FILE
do
  BODY OF THE LOOP
done < <(inotifywait --monitor OTHER ARGUMENTS)

第一个<表示输入重定向,<( )语法表示“执行此命令,将其输出传输到FIFO,并给我FIFO路径,以便我可以从此特殊文件重定向将其输出提供给循环“。

您可以通过以下方式了解我的意思:

echo <(cat </dev/null)

您会看到echo在使用该语法时看到的参数是文件名,可能类似于/dev/fd/XX

摆脱子shell有一个主要优点:循环在主shell范围内执行,因此在循环中执行的变量的任何更改都可以在循环外部看到。这可能并不重要,但是,标记我的话,你会发现它在很多很多情况下所产生的巨大差异。

为了说明子shell的内容,这里有一个小代码片段:

while IFS= read -r line
do
  echo Main $$ $BASHPID
  echo $line
done < <(echo Subshell $$ $BASHPID)

特殊变量$$包含主shell PID,特殊变量BASHPID包含当前子shell(如果没有启动子shell,则包含主shell PID)。您将看到主shell PID在循环和流程替换中是相同的,但BASHPID更改,说明子shell已启动。我认为没有办法摆脱那个子壳。