dpkajqd31574096 2017-01-18 11:47
浏览 88

即使使用PID文件,Unix Bash脚本也会多次启动

I wrote my first bash script, wich is checking folders for changes with the function "inotify" and starts some actions. The whole process is runned by nohup as a backgroundprocess.

The folder is the destination of several Dataloggers, which are pushing files in zip-Format via ftp into different subfolders. The bash script unzips the files and starts a php-script afterwards, which is processing the content of the zip files.

My Problem: Sometimes the bash script gives me errors like the following:

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

This shouldn't happen, because the files exist and I can run the same command in terminal without errors. I had the same problem before, when I accendently ran the script multiple times, so I guess this is somehow causing the problem.

I tried to manage the problem with a PID File, which is located in my home dir. For some reason, it still runs two instances of the bash script. If I try to run another instance, it shows the warning "Process already running" as its supposed to (see program code). When I kill the process of the second instance manually (kill $$), it restarts after a while and again there are two instances of the process running.

#!/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

The Output of ps -ef looks Like this:

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 

As you can see, the second instances Parent-PID is the script itself

EDIT: I changed the bash script as recommended by Fred. The source code now looks like this:

#!/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)

Output of ps -ef still shows two instances:

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条回答 默认 最新

  • dongzhong8691 2017-01-18 17:21
    关注

    You are seeing two lines in the ps output and assumes this means your script was launched twice, but it is not the case.

    You pipe inotifywait into a while loop (which is OK). What you may not realize is that, by doing so, you cause Bash to create a subshell to execute the while loop. That subshell is not a full copy of the whole script.

    If you kill that subshell, because of the while true loop, it gets recreated instantly. Please note that inotifywait has a --monitor option ; I have not studied your script in enough detail, but maybe you could do away with the external while loop by using it.

    There is another way to write the loop that will not eliminate the subshell but has other advantages. Try something like :

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

    The first < indicates input redirection, and the <( ) syntax indicates "execute this command, pipe its output to a FIFO, and give me the FIFO path so that I can redirect from this special file to feed its output to the loop".

    You can get a feel for what I mean by doing :

    echo <(cat </dev/null)
    

    You will see that the argument that echo sees when using that syntax is a file name, probably something like /dev/fd/XX.

    There is one MAJOR advantage to getting rid of the subshell : the loop executes in the main shell scope, so any change in variables you perform in the loop can be seen outside the loop once it terminates. It may not matter much here but, mark my words, you will come to appreciate the enormous difference it makes in many, many situations.

    To illustrate what happens with the subshell, here is a small code snippet :

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

    Special variable $$ contains the main shell PID, and special variable BASHPID contains the current subshell (or the main shell PID if no subshell was launched). You will see that the main shell PID is the same in the loop and in the process substitution, but BASHPID changes, illustrating that a subshell is launched. I do not think there is a way to get rid of that subshell.

    评论

报告相同问题?

悬赏问题

  • ¥15 stata安慰剂检验作图但是真实值不出现在图上
  • ¥15 c程序不知道为什么得不到结果
  • ¥40 复杂的限制性的商函数处理
  • ¥15 程序不包含适用于入口点的静态Main方法
  • ¥15 素材场景中光线烘焙后灯光失效
  • ¥15 请教一下各位,为什么我这个没有实现模拟点击
  • ¥15 执行 virtuoso 命令后,界面没有,cadence 启动不起来
  • ¥50 comfyui下连接animatediff节点生成视频质量非常差的原因
  • ¥20 有关区间dp的问题求解
  • ¥15 多电路系统共用电源的串扰问题