我编写了我的第一个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
答案 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已启动。我认为没有办法摆脱那个子壳。