หมายเลขบรรทัดสคริปต์ R ที่ผิดพลาด?


105

หากฉันเรียกใช้สคริปต์ R แบบยาวจากบรรทัดคำสั่ง (R --slave script.R) ฉันจะทำให้หมายเลขบรรทัดมีข้อผิดพลาดได้อย่างไร

ฉันไม่ต้องการเพิ่มคำสั่ง debug ในสคริปต์ถ้าเป็นไปได้ - ฉันแค่อยากให้ R ทำงานเหมือนภาษาสคริปต์อื่น ๆ ...


31
การปรับปรุงใด ๆ ? สี่ 4 ปีต่อมาดูเหมือนว่าปัญหาจะยังคงมีอยู่แม้ว่าจะมีการนำ R.
Gui Ambros มาใช้

ฉันยังมีสคริปต์ R ที่ยาวมากพร้อมเอาต์พุตขนาดเล็กจำนวนมากฉันต้องการพิมพ์ (ขีดล่าง) (ขีดล่าง) LINE / FILE (ขีดล่าง) (ขีดล่าง) (หมายเลขบรรทัดและชื่อสคริปต์) เช่นนั้นใน C แทนที่จะเข้ารหัสตัวเลขบรรทัดแบบแข็ง เป็นแหล่งที่มา
มอช

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

คำตอบ:


45

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

traceback()

[แก้ไข:]เมื่อเรียกใช้สคริปต์จากบรรทัดคำสั่งคุณจะต้องข้ามการเรียกหนึ่งหรือสองครั้งโปรดดูtraceback () สำหรับเซสชัน R แบบโต้ตอบและไม่โต้ตอบ

ฉันไม่ทราบวิธีอื่นในการดำเนินการนี้หากไม่มีผู้ต้องสงสัยในการแก้ไขข้อบกพร่องตามปกติ:

  1. แก้จุดบกพร่อง ()
  2. เบราว์เซอร์ ()
  3. ตัวเลือก (ข้อผิดพลาด = กู้คืน) [ตามด้วยตัวเลือก (ข้อผิดพลาด = NULL) เพื่อย้อนกลับ]

คุณอาจต้องการดูโพสต์ที่เกี่ยวข้องนี้

[แก้ไข:]ขออภัย ... เพิ่งเห็นว่าคุณกำลังเรียกใช้สิ่งนี้จากบรรทัดคำสั่ง ในกรณีนั้นฉันขอแนะนำให้ใช้ฟังก์ชันตัวเลือก (ข้อผิดพลาด) นี่คือตัวอย่างง่ายๆ:

options(error = quote({dump.frames(to.file=TRUE); q()}))

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

มิฉะนั้นหากมีพื้นที่เฉพาะที่คุณกังวล (เช่นการเชื่อมต่อกับฐานข้อมูล) ให้รวมไว้ในฟังก์ชัน tryCatch ()


โซลูชันที่ลิงก์ในบล็อก [แก้ไข:] แรกใช้ได้ผลสำหรับฉัน แนวทางที่ดีที่สุดน่าจะเป็นความคิดเห็นของ @dshepherd กล่าวคือเพิ่มoptions(error=function() { traceback(2); if(!interactive()) quit("no", status = 1, runLast = FALSE) })(ดูความคิดเห็นของคำตอบที่ยอมรับ) ฉันคิดว่ามันจะสมเหตุสมผลที่จะเพิ่มเข้าไปในคำตอบที่นี่แทนที่จะให้ลิงก์ไปยังเธรดอื่นเท่านั้น
cryo111

1
ตัวเลือกใหม่ที่ให้คุณได้รับหมายเลขบรรทัดใน traceback github.com/aryoda/tryCatchLog
lunguini

13

การทำoptions(error=traceback)จะให้ข้อมูลเพิ่มเติมเล็กน้อยเกี่ยวกับเนื้อหาของบรรทัดที่นำไปสู่ข้อผิดพลาด มันทำให้เกิด traceback #ที่จะปรากฏขึ้นหากมีข้อผิดพลาดและข้อผิดพลาดบางอย่างมันมีจำนวนสายที่นำหน้าด้วย แต่มันถูกหรือพลาดข้อผิดพลาดมากมายจะไม่ได้รับหมายเลขบรรทัด


2
ไม่ค่อยได้ผลสำหรับฉัน ฉันมีไฟล์เพียงไฟล์เดียวและไม่แสดงหมายเลขบรรทัดเพียงแค่บอกว่าNo traceback availableหลังจากเกิดข้อผิดพลาด
Mark Lakata

11

การสนับสนุนสำหรับสิ่งนี้จะมีให้ใน 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


ขอบคุณ. เพิ่งสมัคร r-devel mailing list ด้วย ฉันหลีกเลี่ยง r-help โดยสมมติว่ามันจะอุดตันกล่องจดหมายของฉัน (r-sig-finance ทำแบบนั้นแล้ว)
เชน

1
ไม่เข้าใจวิธีการทำงานจากบรรทัดคำสั่งโดยไม่ต้องใช้สคริปต์ R
Herman Toothrot

1
@hirse: นี่เป็นคำตอบเก่า ๆ ของคุณเกือบสิบคำ ทำไมคุณถึงฟอร์แมตใหม่บนโลกนี้เพื่อแกล้งทำเป็นว่าฉันกำลังอ้างถึง? ฉันไม่ได้และการเปลี่ยนแปลงของคุณไม่ได้สะท้อนถึงเจตนาของฉัน
Dirk Eddelbuettel

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

คุณช่วยเปลี่ยนกลับได้ไหม ขอบคุณ.
Dirk Eddelbuettel

6

คุณทำได้โดยการตั้งค่า

options(show.error.locations = TRUE)

ฉันแค่สงสัยว่าทำไมการตั้งค่านี้จึงไม่ใช่ค่าเริ่มต้นใน R? ควรจะเป็นเช่นเดียวกับในภาษาอื่น ๆ


1
สำหรับข้อมูลพื้นฐานเกี่ยวกับตัวเลือกนี้โปรดดูstat.ethz.ch/R-manual/R-devel/library/base/html/options.html
R Yoda

1
สิ่งนี้เคยได้ผล แต่ถูกปิดใช้งานเนื่องจากไม่น่าเชื่อถือ ฉันคิดว่ามันเป็นความพยายามที่จะบังคับให้คุณใช้ RStudio ซึ่งในที่สุดก็จะไม่ฟรี
Eric Leschinski

6
ฉันสงสัยมัน. R core และ RStudio เป็นองค์กรที่แตกต่างกันมากและโดยเฉพาะอย่างยิ่ง R core เป็น open-sourcers อย่างแข็งขัน
Ben Bolker

ทำงานบน CentOS 6.9, R-3.4.2
irritable_phd_synd ตั้งแต่

อาจจะคุ้มค่าที่จะกล่าวถึงคุณควรตั้งค่าตัวเลือกล่วงหน้าก่อนที่จะจัดหารหัสใด ๆ
JAponte

3

การระบุตัวเลือก global R สำหรับจัดการข้อผิดพลาดที่ไม่ใช่ภัยพิบัติได้ผลสำหรับฉันพร้อมกับเวิร์กโฟลว์ที่กำหนดเองสำหรับการเก็บรักษาข้อมูลเกี่ยวกับข้อผิดพลาดและตรวจสอบข้อมูลนี้หลังจากความล้มเหลว ฉันกำลังใช้งาน R เวอร์ชัน 3.4.1 ด้านล่างนี้ฉันได้รวมคำอธิบายของเวิร์กโฟลว์ที่ใช้งานได้สำหรับฉันรวมถึงรหัสบางอย่างที่ฉันใช้เพื่อตั้งค่าตัวเลือกการจัดการข้อผิดพลาดส่วนกลางใน R

เนื่องจากฉันได้กำหนดค่าไว้การจัดการข้อผิดพลาดยังสร้างไฟล์ RData ที่มีวัตถุทั้งหมดในหน่วยความจำที่ใช้งานได้ในขณะที่เกิดข้อผิดพลาด การถ่ายโอนข้อมูลนี้สามารถอ่านได้กลับเข้ามาใน R โดยใช้แล้วสภาพแวดล้อมต่างๆที่พวกเขามีอยู่ในเวลาของข้อผิดพลาดสามารถตรวจสอบได้โดยใช้การโต้ตอบload()debugger(errorDump)

ฉันจะทราบว่าฉันสามารถรับหมายเลขบรรทัดในtraceback()เอาต์พุตจากฟังก์ชันที่กำหนดเองใด ๆ ภายในสแต็กได้ แต่ถ้าฉันใช้keep.source=TRUEตัวเลือกนี้เมื่อเรียกsource()ใช้ฟังก์ชันแบบกำหนดเองที่ใช้ในสคริปต์ของฉันเท่านั้น หากไม่มีตัวเลือกนี้การตั้งค่าตัวเลือกการจัดการข้อผิดพลาดส่วนกลางดังต่อไปนี้จะส่งเอาต์พุตทั้งหมดของtraceback()ไปยังบันทึกข้อผิดพลาดที่ชื่อerror.logแต่ไม่มีหมายเลขบรรทัด

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

  1. ฉันวางสิ่งต่อไปนี้ไว้ที่ด้านบนของสคริปต์หลักที่ฉันเรียกจากบรรทัดคำสั่ง สิ่งนี้ตั้งค่าตัวเลือกการจัดการข้อผิดพลาดส่วนกลางสำหรับเซสชัน 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()}))
  2. ตรวจสอบให้แน่ใจว่าจากสคริปต์หลักและการเรียกใช้ฟังก์ชันที่ตามมาเมื่อใดก็ตามที่ฟังก์ชันมาจากที่มาระบบkeep.source=TRUEจะใช้ตัวเลือก source('~/path/to/myFunction.R', keep.source=TRUE)นั่นคือที่มาฟังก์ชั่นที่คุณจะใช้ สิ่งนี้จำเป็นเพื่อให้traceback()เอาต์พุตมีหมายเลขบรรทัด ดูเหมือนว่าคุณอาจตั้งค่าตัวเลือกนี้ได้ทั่วโลกโดยใช้options( keep.source=TRUE )แต่ฉันยังไม่ได้ทดสอบเพื่อดูว่าใช้งานได้หรือไม่ หากคุณไม่ต้องการหมายเลขบรรทัดคุณสามารถละเว้นตัวเลือกนี้ได้

  3. จากสถานี (นอก R) Rscript myMainScript.Rโทรสคริปต์หลักในโหมดแบทช์ใช้ นี้จะเริ่มต้นเซสชั่นใหม่ R myMainScript.Rไม่โต้ตอบและเรียกใช้สคริปต์ ข้อมูลโค้ดที่ระบุในขั้นตอนที่ 1 ซึ่งวางไว้ที่ด้านบนสุดของmyMainScript.Rชุดตัวเลือกการจัดการข้อผิดพลาดสำหรับเซสชัน R ที่ไม่โต้ตอบ
  4. พบข้อผิดพลาดบางแห่งภายในการดำเนินการของmyMainScript.R. สิ่งนี้อาจอยู่ในสคริปต์หลักเองหรือหลายฟังก์ชันซ้อนกันอยู่ลึก ๆ เมื่อพบข้อผิดพลาดการจัดการจะดำเนินการตามที่ระบุไว้ในขั้นตอนที่ 1 และเซสชัน R จะสิ้นสุดลง
  5. ไฟล์การถ่ายโอนข้อมูล RData ที่ชื่อerrorDump.rdaและและชื่อบันทึกข้อผิดพลาดerror.logถูกสร้างขึ้นในไดเร็กทอรีที่ระบุโดย'~/myUsername/directoryForDump'ในการตั้งค่าอ็อพชันการจัดการข้อผิดพลาดส่วนกลาง
  6. ในยามว่างตรวจสอบ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)
  7. ในยามว่างของคุณคุณอาจโหลดerrorDump.rdaลงในเซสชั่น R load('~/path/to/errorDump.rda')โต้ตอบโดยใช้ เมื่อโหลดแล้วdebugger(errorDump)ให้เรียกดูวัตถุ R ทั้งหมดในหน่วยความจำในสภาพแวดล้อมที่ใช้งานอยู่ ดูวิธีใช้ R debugger()สำหรับข้อมูลเพิ่มเติม

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


0

ครั้งแรกแล้วoptions(show.error.locations = TRUE) traceback()หมายเลขบรรทัดข้อผิดพลาดจะแสดงหลังจาก #

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