หากฉันเรียกใช้สคริปต์ R แบบยาวจากบรรทัดคำสั่ง (R --slave script.R) ฉันจะทำให้หมายเลขบรรทัดมีข้อผิดพลาดได้อย่างไร
ฉันไม่ต้องการเพิ่มคำสั่ง debug ในสคริปต์ถ้าเป็นไปได้ - ฉันแค่อยากให้ R ทำงานเหมือนภาษาสคริปต์อื่น ๆ ...
หากฉันเรียกใช้สคริปต์ R แบบยาวจากบรรทัดคำสั่ง (R --slave script.R) ฉันจะทำให้หมายเลขบรรทัดมีข้อผิดพลาดได้อย่างไร
ฉันไม่ต้องการเพิ่มคำสั่ง debug ในสคริปต์ถ้าเป็นไปได้ - ฉันแค่อยากให้ R ทำงานเหมือนภาษาสคริปต์อื่น ๆ ...
คำตอบ:
สิ่งนี้จะไม่ให้หมายเลขบรรทัดแก่คุณ แต่จะบอกคุณว่าความล้มเหลวเกิดขึ้นที่ใดในกลุ่มการโทรซึ่งมีประโยชน์มาก:
traceback()
[แก้ไข:]เมื่อเรียกใช้สคริปต์จากบรรทัดคำสั่งคุณจะต้องข้ามการเรียกหนึ่งหรือสองครั้งโปรดดูtraceback () สำหรับเซสชัน R แบบโต้ตอบและไม่โต้ตอบ
ฉันไม่ทราบวิธีอื่นในการดำเนินการนี้หากไม่มีผู้ต้องสงสัยในการแก้ไขข้อบกพร่องตามปกติ:
คุณอาจต้องการดูโพสต์ที่เกี่ยวข้องนี้
[แก้ไข:]ขออภัย ... เพิ่งเห็นว่าคุณกำลังเรียกใช้สิ่งนี้จากบรรทัดคำสั่ง ในกรณีนั้นฉันขอแนะนำให้ใช้ฟังก์ชันตัวเลือก (ข้อผิดพลาด) นี่คือตัวอย่างง่ายๆ:
options(error = quote({dump.frames(to.file=TRUE); q()}))
คุณสามารถสร้างสคริปต์อย่างละเอียดได้ตามที่คุณต้องการในเงื่อนไขข้อผิดพลาดดังนั้นคุณควรตัดสินใจว่าคุณต้องการข้อมูลใดสำหรับการดีบัก
มิฉะนั้นหากมีพื้นที่เฉพาะที่คุณกังวล (เช่นการเชื่อมต่อกับฐานข้อมูล) ให้รวมไว้ในฟังก์ชัน tryCatch ()
options(error=function() { traceback(2); if(!interactive()) quit("no", status = 1, runLast = FALSE) })
(ดูความคิดเห็นของคำตอบที่ยอมรับ) ฉันคิดว่ามันจะสมเหตุสมผลที่จะเพิ่มเข้าไปในคำตอบที่นี่แทนที่จะให้ลิงก์ไปยังเธรดอื่นเท่านั้น
การทำoptions(error=traceback)
จะให้ข้อมูลเพิ่มเติมเล็กน้อยเกี่ยวกับเนื้อหาของบรรทัดที่นำไปสู่ข้อผิดพลาด มันทำให้เกิด traceback #
ที่จะปรากฏขึ้นหากมีข้อผิดพลาดและข้อผิดพลาดบางอย่างมันมีจำนวนสายที่นำหน้าด้วย แต่มันถูกหรือพลาดข้อผิดพลาดมากมายจะไม่ได้รับหมายเลขบรรทัด
No traceback available
หลังจากเกิดข้อผิดพลาด
การสนับสนุนสำหรับสิ่งนี้จะมีให้ใน R 2.10 และใหม่กว่า Duncan Murdoch เพิ่งโพสต์ใน r-devel เมื่อวันที่ 10 กันยายน 2552 เกี่ยวกับfindLineNum และ setBreapoint :
ฉันเพิ่งเพิ่มฟังก์ชั่นสองสามอย่างใน R-devel เพื่อช่วยในการดีบัก
findLineNum()
ค้นหาว่าบรรทัดใดของฟังก์ชันที่สอดคล้องกับซอร์สโค้ดบรรทัดใดบรรทัดหนึ่งsetBreakpoint()
รับเอาต์พุตfindLineNum
และเรียกtrace()
เพื่อตั้งค่าเบรกพอยต์ที่นั่นสิ่งเหล่านี้อาศัยการมีข้อมูลการดีบักการอ้างอิงแหล่งที่มาในโค้ด นี่เป็นค่าเริ่มต้นสำหรับรหัสที่อ่านโดย
source()
แต่ไม่ใช่สำหรับแพ็คเกจ หากต้องการรับการอ้างอิงแหล่งที่มาในรหัสแพ็กเกจให้ตั้งค่าตัวแปรสภาพแวดล้อมR_KEEP_PKG_SOURCE=yes
หรือภายใน R ตั้งค่าoptions(keep.source.pkgs=TRUE)
จากนั้นติดตั้งแพ็กเกจจากซอร์สโค้ด อ่าน?findLineNum
รายละเอียดเกี่ยวกับวิธีบอกให้ค้นหาภายในแพ็กเกจแทนที่จะ จำกัด การค้นหาให้อยู่ในสภาพแวดล้อมส่วนกลางตัวอย่างเช่น,
x <- " f <- function(a, b) { if (a > b) { a } else { b } }" eval(parse(text=x)) # Normally you'd use source() to read a file... findLineNum("<text>#3") # <text> is a dummy filename used by parse(text=)
สิ่งนี้จะพิมพ์
f step 2,3,2 in <environment: R_GlobalEnv>
และคุณสามารถใช้
setBreakpoint("<text>#3")
เพื่อตั้งค่าจุดพักที่นั่น
ยังมีข้อ จำกัด บางอย่าง (และอาจเป็นข้อบกพร่อง) ในโค้ด ฉันจะแก้ไข thos
คุณทำได้โดยการตั้งค่า
options(show.error.locations = TRUE)
ฉันแค่สงสัยว่าทำไมการตั้งค่านี้จึงไม่ใช่ค่าเริ่มต้นใน R? ควรจะเป็นเช่นเดียวกับในภาษาอื่น ๆ
การระบุตัวเลือก global R สำหรับจัดการข้อผิดพลาดที่ไม่ใช่ภัยพิบัติได้ผลสำหรับฉันพร้อมกับเวิร์กโฟลว์ที่กำหนดเองสำหรับการเก็บรักษาข้อมูลเกี่ยวกับข้อผิดพลาดและตรวจสอบข้อมูลนี้หลังจากความล้มเหลว ฉันกำลังใช้งาน R เวอร์ชัน 3.4.1 ด้านล่างนี้ฉันได้รวมคำอธิบายของเวิร์กโฟลว์ที่ใช้งานได้สำหรับฉันรวมถึงรหัสบางอย่างที่ฉันใช้เพื่อตั้งค่าตัวเลือกการจัดการข้อผิดพลาดส่วนกลางใน R
เนื่องจากฉันได้กำหนดค่าไว้การจัดการข้อผิดพลาดยังสร้างไฟล์ RData ที่มีวัตถุทั้งหมดในหน่วยความจำที่ใช้งานได้ในขณะที่เกิดข้อผิดพลาด การถ่ายโอนข้อมูลนี้สามารถอ่านได้กลับเข้ามาใน R โดยใช้แล้วสภาพแวดล้อมต่างๆที่พวกเขามีอยู่ในเวลาของข้อผิดพลาดสามารถตรวจสอบได้โดยใช้การโต้ตอบload()
debugger(errorDump)
ฉันจะทราบว่าฉันสามารถรับหมายเลขบรรทัดในtraceback()
เอาต์พุตจากฟังก์ชันที่กำหนดเองใด ๆ ภายในสแต็กได้ แต่ถ้าฉันใช้keep.source=TRUE
ตัวเลือกนี้เมื่อเรียกsource()
ใช้ฟังก์ชันแบบกำหนดเองที่ใช้ในสคริปต์ของฉันเท่านั้น หากไม่มีตัวเลือกนี้การตั้งค่าตัวเลือกการจัดการข้อผิดพลาดส่วนกลางดังต่อไปนี้จะส่งเอาต์พุตทั้งหมดของtraceback()
ไปยังบันทึกข้อผิดพลาดที่ชื่อerror.log
แต่ไม่มีหมายเลขบรรทัด
นี่คือขั้นตอนทั่วไปที่ฉันทำในเวิร์กโฟลว์ของฉันและวิธีที่ฉันสามารถเข้าถึงการถ่ายโอนข้อมูลหน่วยความจำและบันทึกข้อผิดพลาดหลังจากความล้มเหลวของ R แบบไม่โต้ตอบ
ฉันวางสิ่งต่อไปนี้ไว้ที่ด้านบนของสคริปต์หลักที่ฉันเรียกจากบรรทัดคำสั่ง สิ่งนี้ตั้งค่าตัวเลือกการจัดการข้อผิดพลาดส่วนกลางสำหรับเซสชัน R myMainScript.R
สคริปต์หลักของฉันถูกเรียกว่า บรรทัดต่างๆในโค้ดจะมีความคิดเห็นหลังจากอธิบายสิ่งที่พวกเขาทำ โดยทั่วไปด้วยตัวเลือกนี้เมื่อ R พบข้อผิดพลาดที่ทริกเกอร์stop()
มันจะสร้างไฟล์ดัมพ์ RData (* .rda) ของหน่วยความจำที่ใช้งานได้ในทุกสภาพแวดล้อมที่ใช้งานอยู่ในไดเร็กทอรี~/myUsername/directoryForDump
และจะเขียนบันทึกข้อผิดพลาดที่error.log
มีชื่อพร้อมข้อมูลที่เป็นประโยชน์บางอย่างไปยัง ไดเรกทอรีเดียวกัน คุณสามารถแก้ไขข้อมูลโค้ดนี้เพื่อเพิ่มการจัดการอื่น ๆ เกี่ยวกับข้อผิดพลาด (เช่นเพิ่มการประทับเวลาลงในไฟล์ดัมพ์และชื่อไฟล์บันทึกข้อผิดพลาดเป็นต้น)
options(error = quote({
setwd('~/myUsername/directoryForDump'); # Set working directory where you want the dump to go, since dump.frames() doesn't seem to accept absolute file paths.
dump.frames("errorDump", to.file=TRUE, include.GlobalEnv=TRUE); # First dump to file; this dump is not accessible by the R session.
sink(file="error.log"); # Specify sink file to redirect all output.
dump.frames(); # Dump again to be able to retrieve error message and write to error log; this dump is accessible by the R session since not dumped to file.
cat(attr(last.dump,"error.message")); # Print error message to file, along with simplified stack trace.
cat('\nTraceback:');
cat('\n');
traceback(2); # Print full traceback of function calls with all parameters. The 2 passed to traceback omits the outermost two function calls.
sink();
q()}))
ตรวจสอบให้แน่ใจว่าจากสคริปต์หลักและการเรียกใช้ฟังก์ชันที่ตามมาเมื่อใดก็ตามที่ฟังก์ชันมาจากที่มาระบบkeep.source=TRUE
จะใช้ตัวเลือก source('~/path/to/myFunction.R', keep.source=TRUE)
นั่นคือที่มาฟังก์ชั่นที่คุณจะใช้ สิ่งนี้จำเป็นเพื่อให้traceback()
เอาต์พุตมีหมายเลขบรรทัด ดูเหมือนว่าคุณอาจตั้งค่าตัวเลือกนี้ได้ทั่วโลกโดยใช้options( keep.source=TRUE )
แต่ฉันยังไม่ได้ทดสอบเพื่อดูว่าใช้งานได้หรือไม่ หากคุณไม่ต้องการหมายเลขบรรทัดคุณสามารถละเว้นตัวเลือกนี้ได้
Rscript myMainScript.R
โทรสคริปต์หลักในโหมดแบทช์ใช้ นี้จะเริ่มต้นเซสชั่นใหม่ R myMainScript.R
ไม่โต้ตอบและเรียกใช้สคริปต์ ข้อมูลโค้ดที่ระบุในขั้นตอนที่ 1 ซึ่งวางไว้ที่ด้านบนสุดของmyMainScript.R
ชุดตัวเลือกการจัดการข้อผิดพลาดสำหรับเซสชัน R ที่ไม่โต้ตอบmyMainScript.R
. สิ่งนี้อาจอยู่ในสคริปต์หลักเองหรือหลายฟังก์ชันซ้อนกันอยู่ลึก ๆ เมื่อพบข้อผิดพลาดการจัดการจะดำเนินการตามที่ระบุไว้ในขั้นตอนที่ 1 และเซสชัน R จะสิ้นสุดลงerrorDump.rda
และและชื่อบันทึกข้อผิดพลาดerror.log
ถูกสร้างขึ้นในไดเร็กทอรีที่ระบุโดย'~/myUsername/directoryForDump'
ในการตั้งค่าอ็อพชันการจัดการข้อผิดพลาดส่วนกลางในยามว่างตรวจสอบerror.log
เพื่อตรวจสอบข้อมูลเกี่ยวกับข้อผิดพลาดรวมถึงข้อความแสดงข้อผิดพลาดเองและการติดตามสแต็กทั้งหมดที่นำไปสู่ข้อผิดพลาด นี่คือตัวอย่างของบันทึกที่สร้างขึ้นจากข้อผิดพลาด สังเกตตัวเลขหลัง#
อักขระคือหมายเลขบรรทัดของข้อผิดพลาดที่จุดต่างๆใน call stack:
Error in callNonExistFunc() : could not find function "callNonExistFunc"
Calls: test_multi_commodity_flow_cmd -> getExtendedConfigDF -> extendConfigDF
Traceback:
3: extendConfigDF(info_df, data_dir = user_dir, dlevel = dlevel) at test_multi_commodity_flow.R#304
2: getExtendedConfigDF(config_file_path, out_dir, dlevel) at test_multi_commodity_flow.R#352
1: test_multi_commodity_flow_cmd(config_file_path = config_file_path,
spot_file_path = spot_file_path, forward_file_path = forward_file_path,
data_dir = "../", user_dir = "Output", sim_type = "spot",
sim_scheme = "shape", sim_gran = "hourly", sim_adjust = "raw",
nsim = 5, start_date = "2017-07-01", end_date = "2017-12-31",
compute_averages = opt$compute_averages, compute_shapes = opt$compute_shapes,
overwrite = opt$overwrite, nmonths = opt$nmonths, forward_regime = opt$fregime,
ltfv_ratio = opt$ltfv_ratio, method = opt$method, dlevel = 0)
ในยามว่างของคุณคุณอาจโหลดerrorDump.rda
ลงในเซสชั่น R load('~/path/to/errorDump.rda')
โต้ตอบโดยใช้ เมื่อโหลดแล้วdebugger(errorDump)
ให้เรียกดูวัตถุ R ทั้งหมดในหน่วยความจำในสภาพแวดล้อมที่ใช้งานอยู่ ดูวิธีใช้ R debugger()
สำหรับข้อมูลเพิ่มเติม
เวิร์กโฟลว์นี้มีประโยชน์อย่างมากเมื่อเรียกใช้ R ในสภาพแวดล้อมการผลิตบางประเภทที่คุณมีเซสชัน R ที่ไม่โต้ตอบถูกเริ่มต้นที่บรรทัดคำสั่งและคุณต้องการเก็บข้อมูลเกี่ยวกับข้อผิดพลาดที่ไม่คาดคิดไว้ ความสามารถในการถ่ายโอนข้อมูลหน่วยความจำไปยังไฟล์ที่คุณสามารถใช้เพื่อตรวจสอบหน่วยความจำที่ใช้งานได้ในเวลาที่เกิดข้อผิดพลาดพร้อมกับการมีหมายเลขบรรทัดของข้อผิดพลาดใน call stack ช่วยอำนวยความสะดวกในการดีบักชันสูตรพลิกศพอย่างรวดเร็วของสิ่งที่ทำให้เกิดข้อผิดพลาด
ครั้งแรกแล้วoptions(show.error.locations = TRUE)
traceback()
หมายเลขบรรทัดข้อผิดพลาดจะแสดงหลังจาก #