ฉันจะแก้ปัญหาสคริปต์ Perl CGI ของฉันได้อย่างไร


100

ฉันมีสคริปต์ Perl ที่ใช้งานไม่ได้และฉันไม่รู้ว่าจะเริ่ม จำกัด ปัญหาได้อย่างไร ฉันจะทำอะไรได้บ้าง?


หมายเหตุ: ฉันกำลังเพิ่มคำถามเพราะฉันต้องการเพิ่มคำตอบที่ยาวมากใน Stackoverflow ฉันเชื่อมโยงกับภายนอกในคำตอบอื่น ๆ และสมควรที่จะอยู่ที่นี่ อย่าอายที่จะแก้ไขคำตอบของฉันหากคุณมีสิ่งที่จะเพิ่ม


5
@Evan - จุดของฉันก็คือว่าแม้ในขณะที่ไม่ได้ CW ก็ยังคงเป็นที่สามารถแก้ไขได้ - ถ้าคุณมีกรรมพอ; 100 สำหรับ CW มิฉะนั้น 2k ตอนนี้คุณมีปี 2060 แล้วคุณควรจะแก้ไขโพสต์ที่ไม่ใช่ CW ได้
Marc Gravell

1
@ Evan จุดมหัศจรรย์แสดงอยู่ในคำแนะนำเครื่องมือในคอลัมน์ด้านขวามือที่นี่: stackoverflow.com/privileges
cjm

หากเว็บเบราว์เซอร์ของคุณแสดงสัญญาณรบกวนอาจเป็นการพิมพ์สคริปต์ perl แทน ในกรณีนั้นโปรดดูstackoverflow.com/questions/2621161/…
Andrew Grimm

คำตอบ:


130

คำตอบนี้มีวัตถุประสงค์เพื่อเป็นกรอบทั่วไปสำหรับการทำงานผ่านปัญหากับ Perl สคริปต์ CGI และปรากฏบน Perlmonks แต่เดิมการแก้ไขปัญหาสคริปต์ CGI Perl ไม่ใช่คำแนะนำที่สมบูรณ์สำหรับทุกปัญหาที่คุณอาจพบหรือบทช่วยสอนเกี่ยวกับการกำจัดข้อผิดพลาด มันเป็นเพียงจุดสุดยอดของประสบการณ์ของฉันในการแก้ไขข้อบกพร่องสคริปต์ CGI เป็นเวลายี่สิบปี (บวก!) หน้านี้ดูเหมือนจะมีบ้านหลายหลังและดูเหมือนว่าฉันจะลืมไปแล้วดังนั้นฉันจึงเพิ่มมันลงใน StackOverflow คุณสามารถส่งความคิดเห็นหรือข้อเสนอแนะถึงฉันได้ที่ bdfoy@cpan.org นอกจากนี้ยังเป็นวิกิชุมชน แต่อย่าไปบ้าเกินไป :)


คุณใช้คุณสมบัติในตัวของ Perl เพื่อช่วยคุณค้นหาปัญหาหรือไม่?

เปิดคำเตือนเพื่อให้ Perl เตือนคุณเกี่ยวกับส่วนที่น่าสงสัยของโค้ดของคุณ คุณสามารถทำได้จากบรรทัดคำสั่งด้วย-wสวิตช์ดังนั้นคุณไม่ต้องเปลี่ยนรหัสใด ๆ หรือเพิ่ม pragma ลงในทุกไฟล์:

 % perl -w program.pl

อย่างไรก็ตามคุณควรบังคับตัวเองให้ล้างโค้ดที่น่าสงสัยอยู่เสมอโดยการเพิ่มwarningspragma ลงในไฟล์ทั้งหมดของคุณ:

 use warnings;

หากคุณต้องการข้อมูลเพิ่มเติมนอกเหนือจากข้อความเตือนสั้น ๆ ให้ใช้diagnosticspragma เพื่อรับข้อมูลเพิ่มเติมหรือดูในเอกสารperldiag :

 use diagnostics;

คุณส่งออกส่วนหัว CGI ที่ถูกต้องก่อนหรือไม่?

เซิร์ฟเวอร์คาดหวังให้เอาต์พุตแรกจากสคริปต์ CGI เป็นส่วนหัว CGI โดยปกติที่อาจจะเป็นง่ายๆเป็นprint "Content-type: text/plain\n\n";หรือCGI.pmprint header()และอนุพันธ์ของมัน เซิร์ฟเวอร์บางตัวมีความไวต่อเอาต์พุตข้อผิดพลาด (เปิดSTDERR) ที่แสดงก่อนเอาต์พุตมาตรฐาน (เปิดSTDOUT)

ลองส่งข้อผิดพลาดไปยังเบราว์เซอร์

เพิ่มบรรทัดนี้

 use CGI::Carp 'fatalsToBrowser';

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

บันทึกข้อผิดพลาดบอกว่าอย่างไร

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

สิทธิ์ของสคริปต์คืออะไร?

หากคุณเห็นข้อผิดพลาดเช่น "ปฏิเสธการอนุญาต" หรือ "วิธีที่ไม่ได้ใช้งาน" อาจหมายความว่าสคริปต์ของคุณไม่สามารถอ่านและเรียกใช้งานได้โดยผู้ใช้เว็บเซิร์ฟเวอร์ รสชาติของ Unix เปลี่ยนโหมดไปที่ 755 chmod 755 filenameเป็นที่แนะนำ: อย่าตั้งโหมดเป็น 777!

คุณกำลังใช้use strict?

โปรดจำไว้ว่า Perl จะสร้างตัวแปรโดยอัตโนมัติเมื่อคุณใช้ครั้งแรก นี่เป็นคุณสมบัติ แต่บางครั้งอาจทำให้เกิดข้อบกพร่องหากคุณพิมพ์ชื่อตัวแปรผิด pragma use strictจะช่วยคุณค้นหาข้อผิดพลาดประเภทนั้น มันน่ารำคาญจนกว่าคุณจะชิน แต่การเขียนโปรแกรมของคุณจะดีขึ้นอย่างมากหลังจากนั้นสักครู่และคุณจะมีอิสระที่จะทำผิดพลาดต่างๆ

รวบรวมสคริปต์หรือไม่

คุณสามารถตรวจสอบข้อผิดพลาดในการคอมไพล์ได้โดยใช้-c สวิตช์ จดจ่อกับข้อผิดพลาดแรกที่รายงาน ล้างทำซ้ำ หากคุณได้รับข้อผิดพลาดแปลก ๆ ให้ตรวจสอบให้แน่ใจว่าสคริปต์ของคุณมีการลงท้ายบรรทัดที่ถูกต้อง หากคุณ FTP ในโหมดไบนารีชำระเงินจาก CVS หรืออย่างอื่นที่ไม่จัดการการแปลปลายบรรทัดเว็บเซิร์ฟเวอร์อาจเห็นสคริปต์ของคุณเป็นบรรทัดใหญ่เส้นเดียว โอนสคริปต์ Perl ในโหมด ASCII

สคริปต์บ่นเกี่ยวกับการอ้างอิงที่ไม่ปลอดภัยหรือไม่?

หากสคริปต์ของคุณบ่นเกี่ยวกับการอ้างอิงที่ไม่ปลอดภัยคุณอาจกำลังใช้-Tสวิตช์เพื่อเปิดโหมดที่ไม่ปลอดภัยซึ่งเป็นสิ่งที่ดีเนื่องจากช่วยให้คุณส่งข้อมูลที่ไม่ได้ตรวจสอบไปยังเชลล์ได้ หากมีการร้องเรียนแสดงว่ากำลังทำงานเพื่อช่วยให้เราเขียนสคริปต์ที่ปลอดภัยยิ่งขึ้น ข้อมูลใด ๆ ที่มาจากภายนอกโปรแกรม (เช่นสภาพแวดล้อม) จะถือว่าเป็นมลทิน ตัวแปรสภาพแวดล้อมเช่นPATHและ LD_LIBRARY_PATH เป็นปัญหาโดยเฉพาะอย่างยิ่ง คุณต้องตั้งค่าเหล่านี้เป็นค่าที่ปลอดภัยหรือยกเลิกการตั้งค่าทั้งหมดตามที่ฉันแนะนำ คุณควรใช้เส้นทางที่แน่นอนอยู่แล้ว หากการตรวจสอบสิ่งสกปรกมีการร้องเรียนเกี่ยวกับสิ่งอื่นตรวจสอบให้แน่ใจว่าคุณไม่ได้รับการเคลือบข้อมูล ดู รายละเอียดได้ที่หน้า man perlsec

จะเกิดอะไรขึ้นเมื่อคุณเรียกใช้จากบรรทัดคำสั่ง

สคริปต์แสดงผลลัพธ์ตามที่คุณคาดหวังเมื่อเรียกใช้จากบรรทัดคำสั่งหรือไม่ เอาต์พุตส่วนหัวก่อนตามด้วยบรรทัดว่างหรือไม่ โปรดจำไว้ว่าSTDERRอาจรวมเข้ากับSTDOUT หากคุณอยู่บนเทอร์มินัล (เช่นเซสชันแบบโต้ตอบ) และเนื่องจากการบัฟเฟอร์อาจแสดงขึ้นในลำดับที่สับสน เปิดใช้งานคุณสมบัติฟลัชอัตโนมัติของ Perl โดยตั้งค่า$|เป็นค่าจริง โดยทั่วไปคุณอาจเห็น$|++;ในโปรแกรม CGI เมื่อตั้งค่าแล้วการพิมพ์และการเขียนทุกครั้งจะไปที่เอาต์พุตทันทีแทนที่จะถูกบัฟเฟอร์ คุณต้องตั้งค่านี้สำหรับแต่ละ filehandle ใช้selectเพื่อเปลี่ยนการจัดการไฟล์เริ่มต้นดังนี้:

$|++;                            #sets $| for STDOUT
$old_handle = select( STDERR );  #change to STDERR
$|++;                            #sets $| for STDERR
select( $old_handle );           #change back to STDOUT

ไม่ว่าจะด้วยวิธีใดผลลัพธ์สิ่งแรกควรเป็นส่วนหัว CGI ตามด้วยบรรทัดว่าง

จะเกิดอะไรขึ้นเมื่อคุณเรียกใช้จากบรรทัดคำสั่งที่มีสภาพแวดล้อมเหมือน CGI

โดยปกติสภาพแวดล้อมของเว็บเซิร์ฟเวอร์จะ จำกัด มากกว่าสภาพแวดล้อมบรรทัดคำสั่งของคุณและมีข้อมูลเพิ่มเติมเกี่ยวกับคำขอ หากสคริปต์ของคุณทำงานได้ดีจากบรรทัดคำสั่งคุณอาจลองจำลองสภาพแวดล้อมเว็บเซิร์ฟเวอร์ หากปัญหาปรากฏขึ้นแสดงว่าคุณมีปัญหาด้านสิ่งแวดล้อม

ยกเลิกการตั้งค่าหรือลบตัวแปรเหล่านี้

  • PATH
  • LD_LIBRARY_PATH
  • ORACLE_*ตัวแปรทั้งหมด

ตั้งค่าตัวแปรเหล่านี้

  • REQUEST_METHOD(ตั้งค่าให้GET, HEADหรือPOSTตามความเหมาะสม)
  • SERVER_PORT (ตั้งค่าเป็น 80 โดยปกติ)
  • REMOTE_USER (หากคุณกำลังทำสิ่งที่มีการป้องกันการเข้าถึง)

เวอร์ชันล่าสุดของCGI.pm(> 2.75) ต้องการ-debugแฟล็กเพื่อรับพฤติกรรมเก่า (มีประโยชน์) ดังนั้นคุณอาจต้องเพิ่มลงในCGI.pmการนำเข้าของคุณ

use CGI qw(-debug)

คุณกำลังใช้die()หรือwarn?

ฟังก์ชันเหล่านั้นจะพิมพ์ไปSTDERRจนกว่าคุณจะกำหนดนิยามใหม่ พวกเขาไม่ส่งออกส่วนหัว CGI เช่นกัน คุณสามารถใช้ฟังก์ชันเดียวกันกับแพ็คเกจต่างๆเช่นCGI :: Carp

จะเกิดอะไรขึ้นหลังจากคุณล้างแคชของเบราว์เซอร์

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

สคริปต์ที่คุณคิดว่ามันคืออะไร?

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

คุณกำลังใช้CGI.pmหรืออนุพันธ์ของมัน?

หากปัญหาของคุณเกี่ยวข้องกับการแยกการป้อนข้อมูล CGI และคุณไม่ได้ใช้โมดูลทดสอบกันอย่างแพร่หลายเช่นCGI.pm, CGI::Request, CGI::SimpleหรือCGI::Liteใช้โมดูลและได้รับในชีวิต CGI.pmมีcgi-lib.plโหมดความเข้ากันได้ซึ่งสามารถช่วยคุณแก้ปัญหาการป้อนข้อมูลเนื่องจากการใช้งานตัวแยกวิเคราะห์ CGI รุ่นเก่า

คุณใช้เส้นทางสัมบูรณ์หรือไม่?

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

คุณตรวจสอบค่าส่งคืนหรือไม่

ฟังก์ชัน Perl ส่วนใหญ่จะบอกคุณว่าทำงานหรือไม่และจะตั้งค่าเป็น$!ความล้มเหลว คุณตรวจสอบค่าส่งคืนและตรวจสอบ$!ข้อความแสดงข้อผิดพลาดหรือไม่ คุณตรวจสอบ $@ว่าคุณใช้อยู่evalหรือไม่?

คุณใช้ Perl เวอร์ชันใดอยู่

เวอร์ชันเสถียรล่าสุดของ Perl คือ 5.28 (หรือไม่ขึ้นอยู่กับเวลาที่แก้ไขครั้งล่าสุด) คุณใช้รุ่นเก่ากว่านี้หรือไม่? Perl เวอร์ชันต่างๆอาจมีแนวคิดเกี่ยวกับคำเตือนที่แตกต่างกัน

คุณใช้เว็บเซิร์ฟเวอร์ใด

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

คุณตรวจสอบเอกสารเซิร์ฟเวอร์หรือไม่

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

คุณค้นหาที่เก็บถาวรของcomp.infosystems.www.authoring.cgi?

การใช้งานนี้เป็นประโยชน์ แต่ผู้โพสต์ที่ดีทั้งหมดเสียชีวิตหรือสูญหายไป

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

คุณสามารถสร้างปัญหาซ้ำโดยใช้สคริปต์ทดสอบสั้น ๆ ได้หรือไม่?

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

คุณตัดสินใจไปดูหนังหรือไม่?

อย่างจริงจัง. บางครั้งเราอาจจมอยู่กับปัญหาที่เราพัฒนา "การรับรู้แคบ" (การมองเห็นในอุโมงค์) การหยุดพักดื่มกาแฟสักแก้วหรือระเบิดผู้ร้ายใน [Duke Nukem, Quake, Doom, Halo, COD] อาจทำให้คุณมีมุมมองใหม่ ๆ ที่คุณต้องแก้ไขปัญหาอีกครั้ง

คุณมีปัญหาหรือไม่?

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


4
อย่าอายที่จะแก้ไขคำตอบของฉันหากคุณมีสิ่งที่จะเพิ่ม
brian d foy

ดูเหมือนว่าคุณอาจต้องการลบลิงก์ไปยัง CGI meta FAQ 5.12.1 ถือว่า "เสถียร" หรือไม่
Snake Plissken

1
ทำไมไม่$|=1แทนที่จะเป็น$|++?
Reinierpost

ทำไม$|=1แทนที่จะเป็น$|++? มันไม่ได้สร้างความแตกต่างจริงๆและถึงแม้$|จะมีมนต์ขลัง
brian d foy

2
คำตอบที่ดีฉันคิดว่าควรค่าแก่การกล่าวถึงว่าโซลูชันเหล่านี้บางส่วนควรใช้สำหรับการแก้ไขปัญหาเท่านั้นและไม่ได้ใส่ลงในรหัสการผลิต use strictโดยทั่วไปดีที่จะใช้ตลอดเวลาในขณะที่ใช้อาจจะไม่ได้ให้คำแนะนำในการผลิตโดยเฉพาะอย่างยิ่งถ้าคุณกำลังใช้fatalsToBrowser die
vol7ron


7

คุณใช้ตัวจัดการข้อผิดพลาดในขณะที่คุณกำลังแก้ไขข้อบกพร่องหรือไม่?

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

วิธีหนึ่งที่ทำได้คือโทร

   use CGI::Carp qw(fatalsToBrowser);

ที่ด้านบนสุดของสคริปต์ของคุณ การโทรนั้นจะติดตั้ง$SIG{__DIE__}ตัวจัดการ (ดูที่perlvar ) แสดงข้อผิดพลาดร้ายแรงในเบราว์เซอร์ของคุณโดยเตรียมส่วนหัวที่ถูกต้องไว้ล่วงหน้าหากจำเป็น เคล็ดลับการดีบัก CGI อีกอย่างที่ฉันเคยได้ยินมาก่อนCGI::Carpคือการใช้evalกับDATAและ__END__สิ่งอำนวยความสะดวกบนสคริปต์เพื่อตรวจจับข้อผิดพลาดเวลาคอมไพล์:

   #!/usr/bin/perl
   eval join'', <DATA>;
   if ($@) { print "Content-type: text/plain:\n\nError in the script:\n$@\n; }
   __DATA__
   # ... actual CGI script starts here

เทคนิคที่ละเอียดกว่านี้มีข้อได้เปรียบเล็กน้อยCGI::Carpในการตรวจจับข้อผิดพลาดเวลาคอมไพล์ได้มากขึ้น

อัปเดต:ฉันไม่เคยใช้เลย แต่ดูเหมือนว่าCGI::Debugตามที่ Mikael S แนะนำจะเป็นเครื่องมือที่มีประโยชน์และสามารถกำหนดค่าได้เพื่อจุดประสงค์นี้


3
@Ether: <DATA>เป็นตัวจัดการไฟล์วิเศษที่อ่านสคริปต์ปัจจุบันโดยเริ่มต้นด้วย__END__. Join เป็นการจัดเตรียมบริบทรายการดังนั้น <fh> จึงส่งกลับอาร์เรย์หนึ่งบรรทัดต่อรายการ จากนั้นทำการรวมเข้าด้วยกัน สุดท้ายประเมิน
derobert

@Ether: วิธีที่อ่านง่ายกว่าในการเขียนบรรทัดที่ 2 คือ:eval join(q{}, <DATA>);
derobert

@derobert: จริงๆแล้ว __DATA__ เป็นโทเค็นที่ใช้เริ่มต้นส่วนข้อมูลไม่ใช่ __END__ (ฉันคิดว่านั่นเป็นความสับสนของฉัน)
Ether

1
@Ether: จริงๆแล้วพวกเขาทั้งคู่ทำงานในสคริปต์ระดับบนสุด (ตาม manpage ของ perldata) แต่เนื่องจากเป็นที่ต้องการของDATAฉันจึงเปลี่ยนคำตอบ
derobert

@derobert: ขอบคุณสำหรับลิงค์ doc; ฉันไม่รู้เกี่ยวกับพฤติกรรมความเข้ากันได้ย้อนหลังของ __END__!
Ether

7

ฉันสงสัยว่าทำไมไม่มีใครพูดถึงPERLDB_OPTSตัวเลือกที่เรียกว่าRemotePort; แม้ว่าจะเป็นที่ยอมรับว่ามีตัวอย่างการทำงานบนเว็บRemotePortไม่มากนัก( ไม่ได้กล่าวถึงในperldebug ) - และมันก็เป็นปัญหาสำหรับฉันที่จะสร้างสิ่งนี้ขึ้นมา แต่มันก็เป็นเช่นนั้น (เป็นตัวอย่างของ Linux)

ในการทำตัวอย่างที่เหมาะสมก่อนอื่นฉันต้องการบางสิ่งที่สามารถจำลองเว็บเซิร์ฟเวอร์ CGI ได้ง่ายมากโดยเฉพาะอย่างยิ่งผ่านบรรทัดคำสั่งเดียว หลังจากพบเว็บเซิร์ฟเวอร์บรรทัดคำสั่งแบบง่ายสำหรับเรียกใช้ cgis (perlmonks.org)ฉันพบIO :: All - A Tiny Web Serverที่จะใช้สำหรับการทดสอบนี้

ที่นี่ฉันจะทำงานใน/tmpไดเรกทอรี สคริปต์ CGI จะเป็น/tmp/test.pl(รวมอยู่ด้านล่าง) โปรดทราบว่าIO::Allเซิร์ฟเวอร์จะให้บริการเฉพาะไฟล์ปฏิบัติการในไดเร็กทอรีเดียวกับ CGI ดังนั้นจึงchmod +x test.plจำเป็นต้องใช้ที่นี่ ดังนั้นเพื่อทำการทดสอบ CGI ตามปกติฉันเปลี่ยนไดเร็กทอรีเป็น/tmpในเทอร์มินัลและเรียกใช้เว็บเซิร์ฟเวอร์แบบซับเดียวที่นั่น:

$ cd /tmp
$ perl -MIO::All -e 'io(":8080")->fork->accept->(sub { $_[0] < io(-x $1 ? "./$1 |" : $1) if /^GET \/(.*) / })'

คำสั่งเว็บเซิร์ฟเวอร์จะบล็อกในเทอร์มินัลและจะเริ่มการทำงานของเว็บเซิร์ฟเวอร์ภายในเครื่อง (บน 127.0.0.1 หรือlocalhost) - หลังจากนั้นฉันสามารถไปที่เว็บเบราว์เซอร์และขอที่อยู่นี้:

http://127.0.0.1:8080/test.pl

... และฉันควรสังเกตสิ่งprintที่ทำโดยtest.plการโหลด - และแสดง - ในเว็บเบราว์เซอร์


ตอนนี้ในการดีบักสคริปต์นี้RemotePortก่อนอื่นเราต้องมีผู้ฟังบนเครือข่ายซึ่งเราจะโต้ตอบกับดีบักเกอร์ Perl เราสามารถใช้เครื่องมือบรรทัดคำสั่งnetcat( ncเห็นว่าที่นี่: Perl 如何 remote debug? ) ดังนั้นก่อนอื่นให้เรียกใช้netcatListener ในเทอร์มินัลเดียว - ซึ่งจะบล็อกและรอการเชื่อมต่อบนพอร์ต 7234 (ซึ่งจะเป็นพอร์ตดีบักของเรา):

$ nc -l 7234

จากนั้นเราต้องการperlเริ่มต้นในโหมดดีบักRemotePortเมื่อtest.plมีการเรียกใช้ (แม้ในโหมด CGI ผ่านเซิร์ฟเวอร์) สิ่งนี้ใน Linux สามารถทำได้โดยใช้สคริปต์ "shebang wrapper" ต่อไปนี้ซึ่งจำเป็นต้องมีอยู่ในที่/tmpนี้ด้วยและต้องทำให้สามารถเรียกใช้งานได้:

cd /tmp

cat > perldbgcall.sh <<'EOF'
#!/bin/bash
PERLDB_OPTS="RemotePort=localhost:7234" perl -d -e "do '$@'"
EOF

chmod +x perldbgcall.sh

นี่เป็นเรื่องที่ยุ่งยาก - ดูเชลล์สคริปต์ - ฉันจะใช้ตัวแปรสภาพแวดล้อมใน Shebang ของฉันได้อย่างไร - Unix และ Linux Stack แลกเปลี่ยน แต่เคล็ดลับที่นี่ดูเหมือนว่าจะไม่แยกperlล่ามที่จับtest.pl - ดังนั้นเมื่อเราตีมันเราจะไม่ทำexecแต่เราเรียกว่าperl"ธรรมดา" แทนและโดยพื้นฐานแล้ว "ต้นทาง" test.plสคริปต์ของเราโดยใช้do(ดูฉันจะเรียกใช้ สคริปต์ Perl จากภายในสคริปต์ Perl? )

ตอนนี้เรามีperldbgcall.shใน/tmp- เราสามารถเปลี่ยนtest.plไฟล์เพื่อที่ว่ามันหมายถึงแฟ้มที่ปฏิบัติการนี้ในบรรทัด shebang มัน (แทนการล่าม Perl ปกติ) - นี่คือ/tmp/test.plการแก้ไขดังนี้:

#!./perldbgcall.sh

# this is test.pl

use 5.10.1;
use warnings;
use strict;

my $b = '1';
my $a = sub { "hello $b there" };
$b = '2';
print "YEAH " . $a->() . " CMON\n";
$b = '3';
print "CMON " . &$a . " YEAH\n";

$DB::single=1;  # BREAKPOINT

$b = '4';
print "STEP " . &$a . " NOW\n";
$b = '5';
print "STEP " . &$a . " AGAIN\n";

ตอนนี้ทั้งสองtest.plและผู้ดูแล Shebang ใหม่perldbgcall.shอยู่ใน/tmp; และเราได้ncฟังการเชื่อมต่อดีบักบนพอร์ต 7234 - ดังนั้นในที่สุดเราก็สามารถเปิดหน้าต่างเทอร์มินัลอื่นเปลี่ยนไดเร็กทอรีเป็น/tmpและเรียกใช้เว็บเซิร์ฟเวอร์แบบซับเดียว (ซึ่งจะรับฟังการเชื่อมต่อเว็บบนพอร์ต 8080) ที่นั่น:

cd /tmp
perl -MIO::All -e 'io(":8080")->fork->accept->(sub { $_[0] < io(-x $1 ? "./$1 |" : $1) if /^GET \/(.*) / })'

หลังจากเสร็จสิ้นเราสามารถไปที่เว็บเบราว์เซอร์ของเราและขอที่อยู่http://127.0.0.1:8080/test.plเดิม อย่างไรก็ตามตอนนี้เมื่อเว็บเซิร์ฟเวอร์พยายามเรียกใช้สคริปต์มันจะดำเนินการผ่านperldbgcall.shshebang ซึ่งจะเริ่มต้นperlในโหมดดีบักเกอร์ระยะไกล ดังนั้นการเรียกใช้สคริปต์จะหยุดชั่วคราวและเว็บเบราว์เซอร์จะล็อกรอข้อมูล ตอนนี้เราสามารถเปลี่ยนไปใช้netcatเทอร์มินัลได้แล้วและเราควรเห็นข้อความดีบักเกอร์ Perl ที่คุ้นเคย - อย่างไรก็ตามส่งออกผ่านnc:

$ nc -l 7234

Loading DB routines from perl5db.pl version 1.32
Editor support available.

Enter h or `h h' for help, or `man perldebug' for more help.

main::(-e:1):   do './test.pl'
  DB<1> r
main::(./test.pl:29):   $b = '4';
  DB<1>

ตามที่ตัวอย่างแสดงให้เห็นตอนนี้เราใช้ncเป็น "เทอร์มินัล" โดยทั่วไปดังนั้นเราจึงสามารถพิมพ์r(และ Enter) สำหรับ "run" - และสคริปต์จะเรียกใช้คำสั่งเบรกพอยต์ (ดูเพิ่มเติมใน perl ความแตกต่างระหว่าง $ DB :: single = 1 และ 2? ) ก่อนที่จะหยุดอีกครั้ง (สังเกตจากนั้นเบราว์เซอร์จะยังคงล็อกอยู่)

ตอนนี้เราสามารถพูดได้ว่าก้าวผ่านส่วนที่เหลือtest.plผ่านncเทอร์มินัล:

....
main::(./test.pl:29):   $b = '4';
  DB<1> n
main::(./test.pl:30):   print "STEP " . &$a . " NOW\n";
  DB<1> n
main::(./test.pl:31):   $b = '5';
  DB<1> n
main::(./test.pl:32):   print "STEP " . &$a . " AGAIN\n";
  DB<1> n
Debugged program terminated.  Use q to quit or R to restart,
  use o inhibit_exit to avoid stopping after program termination,
  h q, h R or h o to get additional info.
  DB<1>

... อย่างไรก็ตาม ณ จุดนี้เบราว์เซอร์จะล็อกและรอข้อมูล หลังจากที่เราออกจากโปรแกรมดีบั๊กด้วยq:

  DB<1> q
$

... เบราว์เซอร์หยุดการล็อกหรือไม่ - และในที่สุดก็แสดงผลลัพธ์ (สมบูรณ์) ของtest.pl:

YEAH hello 2 there CMON
CMON hello 3 there YEAH
STEP hello 4 there NOW
STEP hello 5 there AGAIN

แน่นอนว่าการดีบักประเภทนี้สามารถทำได้โดยไม่ต้องใช้งานเว็บเซิร์ฟเวอร์ - อย่างไรก็ตามสิ่งที่ดีที่นี่คือเราไม่ได้สัมผัสกับเว็บเซิร์ฟเวอร์เลย เราทริกเกอร์การเรียกใช้งาน "เนทีฟ" (สำหรับ CGI) จากเว็บเบราว์เซอร์ - และการเปลี่ยนแปลงเดียวที่จำเป็นในสคริปต์ CGI คือการเปลี่ยนแปลงของ shebang (และแน่นอนว่าการมีสคริปต์ shebang wrapper เป็นไฟล์ปฏิบัติการในไฟล์เดียวกัน ไดเรกทอรี)

หวังว่านี่จะช่วยใครสักคน - ฉันแน่ใจว่าจะชอบที่จะสะดุดกับสิ่งนี้แทนที่จะเขียนด้วยตัวเอง:)
ไชโย!


5

สำหรับผมผมใช้log4perl ค่อนข้างมีประโยชน์และง่าย

use Log::Log4perl qw(:easy);

Log::Log4perl->easy_init( { level   => $DEBUG, file    => ">>d:\\tokyo.log" } );

my $logger = Log::Log4perl::get_logger();

$logger->debug("your log message");

1

จริงๆแล้วคุณสามารถทำสิ่งสนุก ๆ เหนือโพสต์นี้ได้ นอกจากนี้วิธีแก้ปัญหาเชิงรุกที่ง่ายที่สุดที่ฉันพบคือเพียงแค่ "พิมพ์"

ในตัวอย่าง: (รหัสปกติ)

`$somecommand`;

เพื่อดูว่ากำลังทำในสิ่งที่ฉันต้องการให้ทำจริงหรือไม่: (ปัญหาในการถ่ายทำ)

print "$somecommand";

1

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

ฉันมักจะทำสิ่งนี้หากทุกอย่างล้มเหลว ฉันจะ SSH เข้าสู่เซิร์ฟเวอร์และเรียกใช้สคริปต์ Perl ด้วยตนเอง ตัวอย่างเช่น:

% perl myscript.cgi 

หากมีปัญหา Perl จะบอกคุณเกี่ยวกับเรื่องนี้ วิธีการดีบักนี้จะกำจัดปัญหาเกี่ยวกับการอนุญาตไฟล์หรือปัญหาเว็บเบราว์เซอร์หรือเว็บเซิร์ฟเวอร์


Perl ไม่ได้บอกหมายเลขบรรทัดที่เกิดข้อผิดพลาดเสมอไป มันบอกหมายเลขบรรทัดที่มันรู้ว่ามีบางอย่างผิดปกติ ข้อผิดพลาดน่าจะเกิดขึ้นแล้ว
brian d foy

0

คุณสามารถรัน perl cgi-script ในเทอร์มินัลโดยใช้คำสั่งด้านล่าง

 $ perl filename.cgi

มันตีความรหัสและให้ผลลัพธ์ด้วยโค้ด HTML มันจะรายงานข้อผิดพลาดถ้ามี


1
ขออภัยคำสั่ง $ perl -c filename.cgi ตรวจสอบไวยากรณ์ของโค้ดและรายงานข้อผิดพลาดหากมี จะไม่ให้รหัส html ของ cgi
D.Karthikeyan

การเรียกใช้perl -c filenameจะตรวจสอบไวยากรณ์เท่านั้น แต่perl filenameพิมพ์ผลลัพธ์ HTML ไม่รับประกันว่าจะไม่มีข้อผิดพลาด 500 CGI แต่เป็นการทดสอบครั้งแรกที่ดี
Nagev
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.