2017-01-18 20 views
1

Я написал свой первый сценарий bash, который проверяет папки для изменений с помощью функции «inotify» и запускает некоторые действия. Весь процесс запускается nohup в качестве фонового процесса.Unix Bash Script запускается несколько раз даже с PID-файлом

Эта папка является местом назначения нескольких регистраторов данных, которые толкают файлы в zip-формате через ftp в разные подпапки. Сценарий bash распаковывает файлы и запускает php-скрипт впоследствии, который обрабатывает содержимое zip-файлов.

Моей проблема: Иногда сценарий Баша дает мне ошибки, как следующее:

- 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 

Выход из пса -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 

Как вы можете видеть, вторые экземпляры Родитель-PID является сценарием самого

EDIT: Я изменил сценарий Баша как рекомендовал Фред. Исходный код теперь выглядит следующим образом:

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

Выхода пса -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

Вы говорите, что сценарий, когда он запускается второй раз, отображает предупреждающее сообщение, а затем не останавливается после утверждения «exit 1»? Если вы действительно пытаетесь «убить $$» в своей интерактивной оболочке, это ничего не делает, поскольку $$ - это PID оболочки и оболочка, отправляющая себя, что сигнал ничего не делает. – Fred

+0

Довольно амбициозный для первого сценария, я должен сказать! Внутри условно-одиночных скобок [] вы всегда должны указывать свои переменные, например [-f "$ PIDFILE"]. Ну, вы должны цитировать их почти везде. Обычно рекомендуется использовать имена переменных в нижнем регистре, чтобы избежать конфликтов с переменными среды (хотя я, как известно, игнорировал это сам ...). – Fred

+0

@Fred Когда я запускаю скрипт во второй раз, он работает так, как он должен работать. Он дает сообщение об ошибке и не запускает процесс. Я не понимаю вашу вторую часть, но то, что я имел в виду при убийстве $$, заключается в том, что я вручную ищу PID с командой «ps -ef», а затем убил его. Спасибо за ваш совет, я изменю его в своем исходном коде. Должен сказать, что я скопировал первую часть с PID-файлом с другого сообщения на форуме, но все равно остальное было довольно сложно, потому что в этом примере я не делал больше действий! – franktank

ответ

0

Вы видите две строки в ps выходе и принимает это означает, что ваш сценарий был запущен в два раза , но это не так.

Вы трубу inotifywait в петлю while (это нормально). То, что вы, возможно, не понимаете, заключается в том, что при этом вы вызываете Bash для создания подоболочки для выполнения цикла while. Эта подоболочка не является полной копией всего скрипта.

Если вы убили эту подоболочку, из-за цикла while true, она мгновенно воссоздается. Обратите внимание, что inotifywait имеет опцию --monitor; Я не изучил ваш сценарий достаточно подробно, но, возможно, вы могли бы покончить с внешним циклом while, используя его.

Существует еще один способ написать цикл, который не устранит подоболочку, но имеет другие преимущества. Попробуйте что-то вроде:

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.

Существует одно ОСНОВНОЕ преимущество избавления от подоболочки: цикл выполняется в основной оболочке, поэтому любое изменение переменных, которые вы выполняете в цикле, можно увидеть вне цикла после его завершения. Это может не иметь большого значения здесь, но, отмечайте мои слова, вы поймете огромную разницу, которую она может иметь во многих, многих ситуациях.

Для того, чтобы проиллюстрировать то, что происходит с субоболочкой, вот небольшой фрагмент кода:

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

Специальными переменная $$ содержит основную оболочку PID, и специальную переменная BASHPID содержит текущую подоболочку (или основная оболочка PID, если не было запущено никаких подоболочек). Вы увидите, что основной ПИД-код оболочки является одним и тем же в цикле и подстановкой процесса, но изменения BASHPID, иллюстрирующие, что запускается подоболочка. Я не думаю, что есть способ избавиться от этой подоболочки.

+0

Спасибо за ваш ответ, я не понимаю его полностью. Вывод <(inotifywait --monitor OTHER ARGUMENTS) передается переменной FILE тогда? Я попытался, я получаю сообщение об ошибке: Ошибка синтаксиса: перенаправление неожиданно. – franktank

+0

Использование <<(COMMAND) выполняет вывод команды COMMAND в цикл. Единственная разница по сравнению с трубой заключается в том, что тело цикла выполняется в объеме текущей оболочки. – Fred

+0

Errorno. 127: Ключ истек .. – franktank

 Смежные вопросы

  • Нет связанных вопросов^_^