Является ли использование «while true» для поддержания подлинности сценария хорошей идеей?


19

Я просто прыгаю в Unix из другого мира, и хотел знать, если

while true
do
  /someperlscript.pl
done

В самом скрипте perl есть средство просмотра папок / файлов, которое выполняется при изменении файлов в целевом местоположении.

Это while trueхорошая идея? Если нет, то какой предпочтительный надежный подход?

ТИА

РЕДАКТИРОВАТЬ: так как это, кажется, вызвало немало интереса, вот полный сценарий. Сценарий perl сам просматривает каталог с помощью средства просмотра файлов. Получив новые файлы (они поступают через rsync), он выбирает новый и обрабатывает его. Теперь входящие файлы могут быть повреждены (не спрашивайте ... поступающие от Raspberry Pi), и иногда процесс может не справиться с этим. Я не знаю точно, почему, потому что мы еще не знаем всех сценариев.

НО - если процесс по какой-то причине завершается неудачно, мы хотим, чтобы он был запущен и работал со следующим файлом, потому что следующий файл полностью не связан с предыдущим, что могло вызвать ошибку.

Обычно я бы использовал какой-то вид перехвата всех и обернул весь код так, чтобы он НИКОГДА не падал. Но не был уверен в Perl.

Из того, что я понял, использование чего-то вроде супервизора является хорошим подходом для этого.


9
Это Linux или UNIX? Если это Linux, вы можете проверить inotifyAPI, чтобы избежать циклов занятости, ожидающих изменения файлов в вашей целевой директории.
роайма

Его Linux, Ubuntu
Абхинав Гуджар

Если вы знаете, что файл хорош до того, как он покинет число «пи», вы можете просто запустить контрольную сумму, такую ​​как md5 или sha1, и отправить ее вместе с вашим файлом. Затем получатель узнает, получил ли он плохой файл, прежде чем попытается его обработать. Если вы этого не знаете, вы все равно можете встроить какую-то частичную или блочную контрольную сумму или что-то подобное в ваш файл данных, который обеспечивает целостность данных, так что вы можете проверять все по ходу, прежде чем совершить процесс, который может дать сбой. Унция профилактики ...
Джо,

Ответы:


21

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

while true
do
  /someperlscript.pl
  sleep 1
done

Это также предотвратит загрузку процессора, если скрипт не найден или сразу завершится сбоем.

Цикл также может быть лучше реализован в самом скрипте perl, чтобы избежать этих проблем.

Редактировать:

Поскольку вы написали цикл, единственной целью является перезапуск сценария perl в случае его сбоя, лучшим подходом было бы реализовать его как отслеживаемую службу, но точный способ сделать это зависит от ОС. Например: Solaris smf, системный Linux или перезапуск на основе cron.


10
Вы могли бы сделать, while sleep 1; do ...чтобы сохранить истинный вызов.
Рафаэль Аренс

4
@RaphaelAhrens Действительно, хотя это немного изменит первоначальное поведение. Альтернатива until ! sleep 1; do ...; doneвсе равно сохранит этот встроенный вызов при немедленном запуске сценария.
Jlliagre

4
Полностью согласен с тем, что следует избегать горячей петли с помощью спящего вызова внутри петли, если вы собираетесь это сделать. Также согласитесь, что управляемый событиями сценарий (inotify, et al) будет лучшим решением. Но использование цикла while не обязательно является злым, если только оно не бесконечно и горячо. Я думаю, что более важная проблема, вероятно, связана с тем, почему Perl-скрипт не работает и должен быть перезапущен.
Крейг

2
Лучше: sleep 1& /someperlscript.pl; wait.
user23013

2
@EliahKagan Хорошо заметили. TBH, я никогда не использую untilинструкцию, я перепутал ее с гипотетическим do/untilциклом, которого нет в оболочке.
jlliagre

13

Другие ответы, касающиеся использования inotify, являются правильными, но не являются ответом на этот вопрос.

Супервизор процесса, например supervisord, upstartили runit, предназначен именно для проблемы смотреть и перезапуск службы , если он выходит из строя.

Ваш дистрибутив, вероятно, поставляется с встроенным супервизором процесса.


11

while trueхороша как универсальная конструкция "петля навсегда". Как говорят другие ответы, тело цикла не должно быть пустым или становиться пустым из-за того, что команда внутри цикла не работает.

Если вы используете Linux, вы можете использовать команду вроде inotifywait, которая делает whileцикл намного проще:

while inotifywait -qqe modify "$DIRECTORY"
do
    process_the_directory "$DIRECTORY"
done

Здесь inotifywaitкоманда будет сидеть и ждать, когда произойдет событие файловой системы (в этом примере, когда файлы в каталоге записываются). В этот момент он успешно завершается, и тело цикла выполняется. Затем снова возвращается к ожиданию. Поскольку inotifywaitкоманда ожидает, что что-то произойдет в каталоге, она намного эффективнее, чем постоянный опрос каталога.


`(apt-get или yum) установите inotify-tools` +1 круто!
JJoao

4

Переместите while 1 в сценарий perl (после предложения @roaima)

#!/usr/bin/perl

 use Linux::Inotify2;

 my $inotify = new Linux::Inotify2 or die "unable to inotify: $!";

 $inotify->watch ("Dir", IN_MODIFY, ## or in_{acess,create,open, etc...}
   sub { my $e = shift;
     my $name = $e->fullname;
     ## whatever 
     print "$name was modified\n" if $e->IN_MODIFY;
  });

 1 while $inotify->poll;

1
Это не соответствует требованию перезапуска, если скрипт по какой-либо причине вылетает или уничтожается.
Jlliagre

@jlliagre, спасибо за комментарий. В ответе я не вижу четкой спецификации для предполагаемого поведения после сбоя или уничтожения. Это явно интересный и актуальный вопрос. На данный момент я сохраню это простым (если сценарий умер или был убит, оставайтесь мертвым :)
JJoao

@jlliagre Вот для чего и исключения. Но Perl может быть не самым подходящим выбором в таком случае, так как он не поддерживает механизм исключений. Просто предположение. Было бы лучше перенести скрипт на язык, который поддерживает исключения. Конечно, все зависит от того, насколько велика работа по портированию.

@Nasha, В Perl, с обработчиками сигналов, переменными ошибок, Tyr :: Tiny и т. Д., Мы можем это сделать! ... но - я ненавижу обработку исключений и исправление ошибок: они никогда не завершаются ...
JJoao

1
@JJoao Я согласен с вами в том, что восстановление после ошибок редко бывает полным. Конечно, разработчик должен охватить все возможные случаи. Так же и с ПЛК в промышленном мире, поэтому это должно быть вполне возможно ;-).

3

Если ваш Perl-скрипт предназначен для постоянной работы, зачем использовать конструкцию while? Когда Perl не удается из-за какой-то серьезной проблемы, новый скрипт Perl, запущенный в то время как может произойти сбой так же сложно. Опять-и-снова и так далее.
если вы действительно хотите, чтобы ваш perl начинался заново, рассмотрите crontab и скрипт, который сначала проверяет наличие запущенных экземпляров. Таким образом, ваш скрипт будет запущен даже после перезагрузки.


2

В общем случае проблем с использованием нет, while trueпоскольку это крошечный тест, который выполняется только после завершения сценария perl. Имейте в виду, что в зависимости от того, какой вариант Linux / Unix вы используете, скрипт может завершить работу при выходе из системы. В таком случае рассмотрите возможность использования цикла в скрипте, вызовите его nohupи поместите в фоновом режиме, т.е.nohup myscript &

Если сценарий perl завершается слишком часто и вызывает нагрузку на процессор, то эта загрузка отвечает за сценарий perl, а не за while true.

Смотрите также man nohupдля деталей.


Единственная проблема заключается в возможности горячей петли, которая увеличивает загрузку процессора. Так что я полностью согласен с тем, чтобы поместить цикл сна в цикл, чтобы приручить его.
Крейг,

2

Если вы хотите управлять процессом, вы можете обратиться к менеджеру процесса, чтобы сделать это.

Во многих современных системах вы можете использовать systemd для мониторинга ваших процессов (что является одним из преимуществ systemd по сравнению с классическими init-скриптами). Если ваш дистрибутив выбора не использует Systemd, вы могли бы использовать DaemonTools или Monit .


monэто простая альтернатива, если неуклюжие громадности monit и systemd беспокоят вас так же сильно, как и меня.
Анко

2

whileЦикл действительно не очень хорошая идея. Там нет никакого выхода - это просто вечно - статически . Любое количество вещей может измениться в окружающей среде, и это не будет затронуто - и это может быть плохо.

Например, если исполняемый файл оболочки, ответственный за этот whileцикл, обновлен, ядро ​​не сможет освободить место на диске для старой версии, пока этот скрипт не закроется, потому что ему нужно поддерживать дескриптор в течение всего времени его работы. И это верно для любых файлов, которые оболочка может открыть по любой причине для запуска цикла - все они будут оставаться открытыми этим whileциклом до тех пор, пока он выполняется - навсегда.

И если, не дай бог, утечка памяти где-нибудь в оболочке, выполняющей этот цикл - даже самую маленькую - она ​​просто продолжит течь - статически . Он будет создан без контроля, и единственный выход - принудительно убить его, а затем запустить его заново, чтобы сделать то же самое позже.

Это действительно не тот способ, которым вы должны настроить фоновый процесс - по крайней мере, на мой взгляд. Вместо этого, как мне кажется, должна быть точка сброса - обновление скрипта и его состояния. Самый простой способ сделать это с exec. Вы можете заменить текущий процесс новым, сохранив тот же PID, но продолжая запуск нового процесса.

Например, если ваш perlскрипт должен возвращать true после того, как он успешно обработал изменение файла в некотором отслеживаемом каталоге:

#!/bin/sh
trap 'rm -rf -- "${ldir%%*.}"' 0 INT
_exec() case    $#      in
        (0)     exec env -  "PID=$$" "ldir=${TMPDIR:-/tmp}/." \
                            "$0" "$@";;
        (*)     export "$@" "boff=0" "lmt=30"
                exec        "$0" "$@";;
        esac
[ "$PID" = "$$" ] || _exec
[ -w "$ldir" ]  &&
case    $ldir   in
(*.)    until   mkdir -- "$ldir"
        do      :& ldir=$ldir$$$!
        done    2>/dev/null
;;
(*)     until   /someperlscript.pl ||
                [ "$((boff+=1))"  -ge "$lmt" ]
        do      [ -d "$ldir" ]     &&
                sleep "$boff"      || ! break
        done
;;esac  &&      _exec ldir PID

...или что-то типа того. Что-то, что позволяет базовому механизму за циклом обновляться время от времени.


1

Я проголосовал за некоторые из этих ответов, но у меня инстинктивно самые теплые чувства к ответу @ WalterA. Ну, я сделал, пока я не создал свой собственный ответ ...

Лично я бы хотел обновить скрипт Perl, чтобы он записывал описательную запись в журнале о сбое и отправлял предупреждение администратору.

Если сценарий Perl предназначен для продолжения работы в ожидании событий изменения из файловой системы, почему он не работает?

Если это не дает сбоя, почему вы беспокоитесь о том, чтобы обернуть его в скрипт, чтобы перезапустить его бесконечно?

Если есть проблема конфигурации или какая-то нарушенная зависимость, которая приводит к прерыванию Perl-сценария, простой перезапуск его снова и снова вряд ли заставит его внезапно начать работать.

Вы знаете определение безумия, верно? (делать одно и то же снова и снова, ожидая другого результата). Просто говорю. ;-)


ха-ха, я знаю, я знаю. но вы знаете - реальный мир - отстой. В любом случае - причина в том, что он обрабатывает файлы, и некоторые файлы могут даже не передаваться правильно. Мы еще не знаем, по каким причинам он может потерпеть неудачу. Я понимаю вашу точку зрения по поводу регистрации ошибки, но мне все равно потребуется ее резервное копирование для обработки следующего файла через что-то вроде supervisord
Abhinav Gujjar

Если вы можете перехватить исключения, вы можете записать их в журнал, затем продолжить обработку и даже вернуться назад и время от времени пытаться повторить неудачные файлы? Возможно, сбойный файл был заблокирован другим пользователем или процессом, когда вы пытались его обработать, и т. Д.
Крейг,
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.