การใช้ "ในขณะที่จริง" เพื่อให้สคริปต์มีชีวิตอยู่เป็นความคิดที่ดี?


19

ฉันแค่กระโดดเข้าสู่ยูนิกซ์จากโลกที่แตกต่างและอยากรู้ว่า

while true
do
  /someperlscript.pl
done

สคริปต์ perl ภายในมีโฟลเดอร์ / ตัวเฝ้าดูไฟล์ที่ดำเนินการเมื่อไฟล์ถูกเปลี่ยนในตำแหน่งเป้าหมาย

นี่while trueเป็นความคิดที่ดีหรือไม่? ถ้าไม่เป็นวิธีการที่แข็งแกร่งที่ต้องการคืออะไร?

TIA

แก้ไข: เนื่องจากสิ่งนี้ดูเหมือนว่าจะสร้างความสนใจพอสมควรนี่เป็นสถานการณ์ที่สมบูรณ์ สคริปต์ perl ตัวเองดูไดเรกทอรีโดยใช้ตัวตรวจสอบไฟล์ เมื่อได้รับไฟล์ใหม่ (พวกเขามาถึงทาง rsync) มันจะรับไฟล์ใหม่และดำเนินการ ตอนนี้ไฟล์ที่เข้ามาอาจเสียหาย (อย่าถาม .. มาจาก raspberry pi) และบางครั้งกระบวนการอาจไม่สามารถจัดการกับมันได้ ฉันไม่รู้ว่าทำไมเพราะเรายังไม่ได้ตระหนักถึงสถานการณ์ทั้งหมด

แต่ - หากกระบวนการล้มเหลวด้วยเหตุผลบางอย่างเราต้องการให้มันทำงานและจัดการกับไฟล์ถัดไปเพราะไฟล์ถัดไปไม่เกี่ยวข้องอย่างสมบูรณ์กับไฟล์ก่อนหน้าซึ่งอาจทำให้เกิดข้อผิดพลาด

โดยปกติฉันจะใช้การจับบางอย่างและห่อโค้ดทั้งหมดไว้รอบ ๆ เพื่อไม่ให้เกิดปัญหา แต่ก็ไม่แน่ใจเรื่อง Perl

จากสิ่งที่ฉันเข้าใจการใช้สิ่งต่าง ๆ เช่น supervisord เป็นวิธีการที่ดีสำหรับสิ่งนี้


9
นี่เป็น Linux หรือ UNIX หรือไม่ ถ้าเป็น Linux คุณอาจต้องการตรวจสอบinotifyAPI เพื่อให้คุณสามารถหลีกเลี่ยงการวนรอบไม่ว่างที่รอให้ไฟล์เปลี่ยนแปลงในไดเรกทอรีเป้าหมายของคุณ
roaima

ลินุกซ์ของมันอูบุนตู
Abhinav Gujjar

หากคุณรู้ว่าไฟล์ดีก่อนที่จะออกจาก pi คุณสามารถเรียกใช้ checksum เช่น md5 หรือ sha1 แล้วส่งไฟล์นั้นไปพร้อมกับไฟล์ของคุณ จากนั้นตัวรับสัญญาณจะรู้ว่ามันมีไฟล์เสียหรือไม่ก่อนที่จะพยายามประมวลผล หากคุณไม่ทราบว่าคุณยังสามารถสร้างการตรวจสอบบางส่วนหรือบล็อกการตรวจสอบหรือสิ่งที่คล้ายกับไฟล์ข้อมูลของคุณซึ่งรับประกันความถูกต้องของข้อมูลดังนั้นคุณสามารถตรวจสอบสิ่งต่าง ๆ ตามที่คุณไปก่อนที่จะยอมรับกระบวนการที่อาจล้มเหลว ออนซ์ของการป้องกัน ...
Joe

คำตอบ:


21

ขึ้นอยู่กับความเร็วของสคริปต์ Perl ที่คืนมา หากมันกลับมาอย่างรวดเร็วคุณอาจต้องการแทรกการหยุดชั่วคราวเล็กน้อยระหว่างการประมวลผลเพื่อหลีกเลี่ยงการโหลด CPU เช่น:

while true
do
  /someperlscript.pl
  sleep 1
done

วิธีนี้จะป้องกัน CPU hog หากไม่พบสคริปต์หรือขัดข้องทันที

การวนซ้ำอาจทำได้ดีกว่าในสคริปต์ Perl เพื่อหลีกเลี่ยงปัญหาเหล่านี้

แก้ไข:

ในขณะที่คุณเขียนลูปวัตถุประสงค์เพียงอย่างเดียวคือการรีสตาร์ทสคริปต์ perl หากมันล้มเหลวแนวทางที่ดีกว่าคือการใช้มันเป็นบริการที่ได้รับการตรวจสอบ แต่วิธีที่แม่นยำในการขึ้นอยู่กับระบบปฏิบัติการ เช่น Solaris smf, Linux systemd หรือ cron based restarter


10
คุณสามารถทำได้while sleep 1; do ...เพื่อบันทึกการโทรที่แท้จริง
Raphael Ahrens

4
@RaphaelAhrens แน่นอนว่าแม้ว่าจะเปลี่ยนพฤติกรรมเริ่มต้นเล็กน้อย ทางเลือกuntil ! sleep 1; do ...; doneจะยังคงบันทึกการโทรในตัวขณะที่เริ่มสคริปต์ทันที
jlliagre

4
ทั้งหมดเห็นด้วยอย่างยิ่งว่าควรหลีกเลี่ยงการวนรอบฮ็อตด้วยการโทรแบบสลีปภายในลูปถ้าคุณกำลังจะทำ ยังยอมรับด้วยว่าสคริปต์ที่ขับเคลื่อนโดยเหตุการณ์ (inotify, et al) จะเป็นทางออกที่ดีกว่า แต่การใช้ลูปในขณะนั้นไม่ใช่ความชั่วร้ายภายในจำเป็นต้องใช้เว้นแต่จะไม่มีที่สิ้นสุดและร้อน ฉันคิดว่าปัญหาที่สำคัญกว่านั้นน่าจะเกี่ยวข้องกับสาเหตุที่สคริปต์ Perl ไม่ทำงานและจำเป็นต้องเริ่มต้นใหม่
Craig

2
sleep 1& /someperlscript.pl; waitที่ดีกว่า:
user23013

2
@EliahKagan เห็นดี TBH ฉันไม่เคยใช้untilคำสั่งฉันสับสนกับdo/untilวงสมมุติที่ไม่มีอยู่ในเปลือก
jlliagre

13

คำตอบอื่น ๆ เกี่ยวกับการใช้inotifyถูกต้อง แต่ไม่ใช่คำตอบสำหรับคำถามนี้

หัวหน้างานกระบวนการเช่นsupervisord, upstartหรือrunitถูกออกแบบมาสำหรับตรงปัญหาในการรับชมและรีสตาร์ทบริการถ้ามันเกิดปัญหา

distro ของคุณอาจมาพร้อมกับผู้ควบคุมกระบวนการในตัว


11

while trueก็ดีเหมือนการก่อสร้างแบบ "วนตลอดไป" สำหรับจุดประสงค์ทั่วไป ดังที่คำตอบอื่น ๆ บอกว่าร่างกายของลูปไม่ควรว่างเปล่าหรือว่างเปล่าโดยอาศัยคำสั่งภายในลูปที่ไม่ทำงาน

หากคุณใช้ Linux คุณอาจต้องการใช้คำสั่งinotifywaitที่ทำให้whileลูปง่ายขึ้นมาก:

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

ที่นี่inotifywaitคำสั่งจะนั่งรอเหตุการณ์ระบบไฟล์ที่จะเกิดขึ้น (ในตัวอย่างนี้เมื่อไฟล์ในไดเรกทอรีถูกเขียนไป) ณ จุดนั้นมันจะออกสำเร็จและเนื้อความของลูปจะดำเนินการ จากนั้นกลับไปรออีกครั้ง เนื่องจากinotifywaitคำสั่งรอให้มีบางสิ่งเกิดขึ้นในไดเรกทอรีจึงมีประสิทธิภาพมากกว่าการสำรวจไดเร็กทอรีอย่างต่อเนื่อง


`(apt-get or yum) ติดตั้ง inotify-tools` +1 ยอดเยี่ยม!
JJoao

4

ย้ายสักครู่ 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
@Joao ฉันเห็นด้วยกับคุณเกี่ยวกับข้อเท็จจริงที่ว่าการกู้คืนข้อผิดพลาดไม่ค่อยสมบูรณ์ แน่นอนว่ามันขึ้นอยู่กับผู้พัฒนาเพื่อให้ครอบคลุมกรณีที่เป็นไปได้ทั้งหมด ดังนั้นมันจึงเป็น PLC ในโลกอุตสาหกรรมดังนั้นมันจะต้องเป็นไปได้ทีเดียว ;-)

3

เมื่อสคริปต์ Perl ของคุณมีวัตถุประสงค์เพื่อให้ทำงานตลอดเวลาทำไมใช้ในขณะที่การก่อสร้าง? เมื่อ perl ล้มเหลวในมุมมองของปัญหาร้ายแรงสคริปต์ perl ใหม่ที่เริ่มต้นในขณะที่อาจล้มเหลวอย่างหนัก อีกครั้งและอีกครั้งและสิ่งที่เกี่ยวกับ
หากคุณต้องการให้ perl ของคุณเริ่มต้นใหม่ให้ลองพิจารณา crontab และสคริปต์ที่ตรวจสอบการใช้งานอินสแตนซ์ก่อน วิธีนี้สคริปต์ของคุณจะเริ่มต้นหลังจากรีบูต


2

โดยทั่วไปจะไม่มีปัญหาในการใช้while trueเนื่องจากเป็นการทดสอบเล็ก ๆ ซึ่งจะดำเนินการหลังจากสคริปต์ perl สิ้นสุดลงเท่านั้น โปรดทราบว่าสคริปต์ Linux / Unix ที่คุณใช้นั้นขึ้นอยู่กับว่าสคริปต์นั้นถูกยกเลิกเมื่อออกจากระบบ ในกรณีดังกล่าวให้พิจารณาใช้การวนรอบในสคริปต์และเรียกมันด้วยnohupและวางไว้ในพื้นหลังเช่นnohup myscript &

หาก Perl สคริปต์ยุติบ่อยเกินไปและทำให้เกิดภาระของ CPU กว่าภาระนี้มีความรับผิดชอบต่อ Perl while trueสคริปต์และไม่

ดูman nohupรายละเอียดเพิ่มเติมที่


ปัญหาเดียวคือศักยภาพของ hot loop ที่ทำให้การใช้งาน CPU สูงขึ้น ดังนั้นฉันจึงเห็นด้วยกับการวางสายการนอนหลับภายในวงเพื่อทำให้เชื่องลง
Craig

2

หากคุณต้องการจัดการกระบวนการคุณอาจต้องมองหาผู้จัดการกระบวนการเพื่อทำเช่นนั้น

ด้วยระบบล่าสุดจำนวนมากคุณสามารถใช้systemdเพื่อตรวจสอบกระบวนการของคุณ (ซึ่งเป็นหนึ่งในประโยชน์ของ systemd ผ่านสคริปต์เริ่มต้นแบบคลาสสิก) หาก distro ที่คุณเลือกไม่ได้ใช้ systemd คุณสามารถใช้daemontoolsหรือmonit


mon เป็นทางเลือกง่าย ๆ ถ้าความวุ่นวายของ monit และ systemd รบกวนคุณมากเท่ากับที่ฉันทำ
Anko

2

การwhileวนซ้ำไม่ใช่ความคิดที่ดีจริงๆ ไม่มีการหลบหนีอยู่ที่นั่น - มันก็วิ่งไปตลอดกาล - แบบคงที่ สิ่งต่าง ๆ สามารถเปลี่ยนแปลงได้ในสภาพแวดล้อมและจะไม่ได้รับผลกระทบ - และสิ่งนี้อาจไม่ดี

ตัวอย่างเช่นถ้าเชลล์ที่สามารถเรียกใช้งานได้ที่รับผิดชอบการwhileวนรอบนั้นเคอร์เนลจะไม่สามารถปล่อยพื้นที่ว่างในดิสก์สำหรับเวอร์ชันเก่าจนกว่าสคริปต์นั้นจะหยุดทำงานเนื่องจากจะต้องรักษา descriptor ไว้ตราบเท่าที่รัน และนั่นเป็นความจริงสำหรับไฟล์ใด ๆ ที่เชลล์อาจเปิดด้วยเหตุผลใดก็ตามที่รันลูป - พวกมันจะถูกเปิดโดยwhileลูปนี้ตราบใดที่มันรัน - ตลอดไป

และถ้าห้ามสวรรค์มีหน่วยความจำได้ทุกที่รั่วไหลในเปลือกทำงานที่ห่วง - แม้นาทีมากที่สุด - มันก็จะยังคงรั่วไหล - แบบคงที่ มันจะสร้างไม่ จำกัด และสิ่งเดียวที่จะช่วยฆ่ามันได้ก็คือเริ่มต้นใหม่เพื่อทำสิ่งเดียวกันในภายหลัง

นี่ไม่ใช่วิธีที่คุณควรตั้งค่ากระบวนการพื้นหลัง - อย่างน้อยไม่ใช่ในความคิดของฉัน อย่างที่ฉันคิดว่าควรมีจุดรีเซ็ต - การรีเฟรชสคริปต์และสถานะของมัน execวิธีที่ง่ายที่สุดที่จะทำนี้ด้วย คุณสามารถแทนที่กระบวนการปัจจุบันด้วยกระบวนการใหม่ - ในขณะที่รักษา PID เดียวกัน - แต่ยังคงเรียกใช้กระบวนการใหม่

ตัวอย่างเช่นหากperlสคริปต์ของคุณควรกลับเป็นจริงหลังจากจัดการจัดการการแก้ไขไฟล์ในไดเรกทอรีที่ได้รับการตรวจสอบเรียบร้อยแล้ว:

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

หากคุณสามารถตรวจจับข้อยกเว้นได้คุณสามารถบันทึกได้จากนั้นทำการประมวลผลต่อไปและแม้กระทั่งย้อนกลับและลองไฟล์ที่ล้มเหลวเป็นครั้งคราว? บางทีไฟล์ที่ล้มเหลวถูกล็อกโดยผู้ใช้หรือกระบวนการอื่นเมื่อคุณพยายามที่จะดำเนินการได้ ฯลฯ
เครก
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.