R Skriptzeilennummern bei Fehler?

103

Wenn ich ein langes R-Skript über die Befehlszeile ausführe (R --slave script.R), wie kann ich es dazu bringen, bei Fehlern Zeilennummern anzugeben?

Ich möchte dem Skript keine Debug-Befehle hinzufügen, wenn dies überhaupt möglich ist. Ich möchte nur, dass sich R wie die meisten anderen Skriptsprachen verhält.

Gabel und warten
quelle
31
Irgendwelche Updates? Vier 4 Jahre später scheint das Problem trotz der allgemeinen Annahme von R.
Gui Ambros
Ich habe auch ein sehr langes R-Skript mit vielen kleinen Ausgaben. Ich möchte LINE / FILE (Unterstrich) (Unterstrich) (Zeilennummern und Skriptname) wie in C drucken (Unterstrich) (Unterstrich), anstatt Zeilennummern fest zu codieren in die Quelle.
Mosh
Ich weiß nicht, ob R intern wirklich eine Vorstellung von 'Zeilennummern' hat. Es gibt jedoch eine Vorstellung von vollständigen Aufgaben, dh Aufgaben der obersten Ebene. Man könnte zum Beispiel leicht einen Task-Handler definieren, um einem mitzuteilen, welche Top-Level-Task fehlgeschlagen ist. Das ist natürlich kein großer Trost für diejenigen mit großen Ketten oder großen bedingten Aussagen.
Russellpierce

Antworten:

44

Dies gibt Ihnen nicht die Zeilennummer, sondern zeigt Ihnen, wo der Fehler im Aufrufstapel auftritt, was sehr hilfreich ist:

traceback()

[Bearbeiten:] Wenn Sie ein Skript über die Befehlszeile ausführen, müssen Sie ein oder zwei Aufrufe überspringen. Siehe traceback () für interaktive und nicht interaktive R-Sitzungen

Mir ist kein anderer Weg bekannt, dies ohne die üblichen Debugging-Verdächtigen zu tun:

  1. debuggen()
  2. Browser()
  3. Optionen (Fehler = Wiederherstellen) [gefolgt von Optionen (Fehler = NULL) zum Zurücksetzen]

Vielleicht möchten Sie sich diesen verwandten Beitrag ansehen.

[Bearbeiten:] Entschuldigung ... habe gerade gesehen, dass Sie dies über die Befehlszeile ausführen. In diesem Fall würde ich vorschlagen, mit den Optionen (Fehler) zu arbeiten. Hier ist ein einfaches Beispiel:

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

Sie können ein Skript unter einer Fehlerbedingung so ausführlich erstellen, wie Sie möchten. Sie sollten also nur entscheiden, welche Informationen Sie zum Debuggen benötigen.

Wenn Sie bestimmte Bereiche betreffen (z. B. eine Verbindung zu einer Datenbank herstellen), schließen Sie diese in eine tryCatch () - Funktion ein.

Shane
quelle
Die im ersten Block [Bearbeiten:] verknüpfte Lösung funktioniert für mich. Der beste Ansatz scheint der Kommentar von @dshepherd zu sein, dh hinzufügen options(error=function() { traceback(2); if(!interactive()) quit("no", status = 1, runLast = FALSE) })(siehe Kommentar der akzeptierten Antwort). Ich denke, es wäre sinnvoll, es der Antwort hier hinzuzufügen, anstatt nur einen Link zu einem anderen Thread bereitzustellen.
cryo111
1
Eine neue Option, mit der Sie die Zeilennummern im
Traceback erhalten können.
13

Wenn Sie dies tun, erhalten Sie options(error=traceback)etwas mehr Informationen über den Inhalt der Zeilen, die zum Fehler geführt haben. Bei einem Fehler wird ein Traceback angezeigt. Bei einigen Fehlern wird die Zeilennummer vorangestellt #. Aber es ist ein Hit oder Miss, viele Fehler erhalten keine Zeilennummern.

Hugh Perkins
quelle
2
Funktioniert bei mir nicht ganz. Ich habe nur eine Datei und sie zeigt nicht die Zeilennummer, sondern nur No traceback availablenach dem Fehler.
Mark Lakata
11

Unterstützung dafür wird in R 2.10 und höher bereitgestellt. Duncan Murdoch hat gerade am 10. September 2009 bei r-devel über findLineNum und setBreapoint gepostet :

Ich habe R-devel gerade einige Funktionen hinzugefügt, um beim Debuggen zu helfen. findLineNum()findet heraus, welche Zeile welcher Funktion einer bestimmten Zeile des Quellcodes entspricht; setBreakpoint()Nimmt die Ausgabe von findLineNumund ruft trace()auf, um dort einen Haltepunkt festzulegen.

Diese basieren auf Debug-Informationen zur Quellreferenz im Code. Dies ist die Standardeinstellung für Code, der von gelesen wird source(), jedoch nicht für Pakete. Um die Quellreferenzen im Paketcode abzurufen, legen Sie die Umgebungsvariable fest R_KEEP_PKG_SOURCE=yesoder setzen Sie innerhalb von R options(keep.source.pkgs=TRUE)das Paket aus dem Quellcode. Lesen Sie, ?findLineNumwie Sie festlegen können, dass innerhalb von Paketen gesucht werden soll, anstatt die Suche auf die globale Umgebung zu beschränken.

Beispielsweise,

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=)

Dies wird gedruckt

 f step 2,3,2 in <environment: R_GlobalEnv>

und du kannst verwenden

setBreakpoint("<text>#3")

dort einen Haltepunkt setzen.

Es gibt immer noch einige Einschränkungen (und wahrscheinlich Fehler) im Code; Ich werde das reparieren

Dirk Eddelbuettel
quelle
Vielen Dank. Ich habe mich gerade für die R-Devel-Mailingliste angemeldet. Ich habe R-Help unter der Annahme vermieden, dass es meinen Posteingang verstopfen würde (R-Sig-Finance macht das bereits).
Shane
1
Ich
1
@hirse: Dies ist eine fast zehn deine alte Antwort. Warum um alles in der Welt haben Sie es neu formatiert, um so zu tun, als würde ich zitieren? Ich war es nicht und deine Veränderung spiegelt nicht meine Absicht wider.
Dirk Eddelbuettel
"Duncan Murdoch hat gerade gepostet:" klingt sehr nach einem Zitat, aber wenn das nicht korrekt ist, setzen Sie die Bearbeitung bitte zurück. Ich wollte es für mich lesbarer machen und überprüfte das Datum erst, als ich fertig war. Wenn die gesamte Antwort zu veraltet ist, können Sie sie auch löschen, um Verwirrung bei zukünftigen Lesern zu vermeiden.
Hirse
Können Sie es bitte zurücksetzen? Danke dir.
Dirk Eddelbuettel
6

Sie tun es durch Einstellen

options(show.error.locations = TRUE)

Ich frage mich nur, warum diese Einstellung in R keine Standardeinstellung ist. Es sollte so sein, wie es in jeder anderen Sprache ist.

TMS
quelle
1
Hintergrundinformationen zu dieser Option finden Sie unter stat.ethz.ch/R-manual/R-devel/library/base/html/options.html
R Yoda
1
Dies funktionierte früher, wurde jedoch deaktiviert, da es nicht zuverlässig ist. Ich denke, es ist ein Versuch, Sie zu zwingen, das RStudio zu verwenden, das irgendwann nicht mehr frei sein wird.
Eric Leschinski
6
Ich bezweifle das. R-Core und RStudio sind sehr unterschiedliche Organisationen, und insbesondere R-Core sind überzeugte Open-Sourcer.
Ben Bolker
Arbeitete an CentOS 6.9, R-3.4.2
irritable_phd_syndrom
Vielleicht ist es erwähnenswert, dass Sie die Optionen im Voraus festlegen sollten, bevor Sie Code beziehen.
JAponte
3

Die Angabe der globalen R-Option für die Behandlung nicht katastrophaler Fehler hat bei mir funktioniert, zusammen mit einem angepassten Workflow zum Speichern von Informationen über den Fehler und zum Überprüfen dieser Informationen nach dem Fehler. Ich verwende derzeit R Version 3.4.1. Im Folgenden habe ich eine Beschreibung des für mich funktionierenden Workflows sowie einen Code eingefügt, mit dem ich die globale Fehlerbehandlungsoption in R festgelegt habe.

Wie ich es konfiguriert habe, erstellt die Fehlerbehandlung auch eine RData-Datei, die alle Objekte im Arbeitsspeicher zum Zeitpunkt des Fehlers enthält. Dieser Speicherauszug kann mithilfe von R zurückgelesen werden, load()und dann können die verschiedenen Umgebungen, wie sie zum Zeitpunkt des Fehlers vorhanden waren, mithilfe interaktiv überprüft werden debugger(errorDump).

Ich werde bemerken, dass ich in der traceback()Ausgabe Zeilennummern von allen benutzerdefinierten Funktionen innerhalb des Stapels erhalten konnte, aber nur, wenn ich die keep.source=TRUEOption beim Aufrufen source()von benutzerdefinierten Funktionen verwendet habe, die in meinem Skript verwendet wurden. Ohne diese Option wurde durch Festlegen der globalen Fehlerbehandlungsoption wie unten die vollständige Ausgabe von traceback()an ein Fehlerprotokoll mit dem Namen gesendet error.log, aber die Zeilennummern waren nicht verfügbar.

Hier sind die allgemeinen Schritte, die ich in meinem Workflow unternommen habe, und wie ich nach einem nicht interaktiven R-Fehler auf den Speicherauszug und das Fehlerprotokoll zugreifen konnte.

  1. Ich habe Folgendes oben in das Hauptskript eingefügt, das ich über die Befehlszeile aufgerufen habe. Dies legt die globale Fehlerbehandlungsoption für die R-Sitzung fest. Mein Hauptskript wurde aufgerufen myMainScript.R. Die verschiedenen Zeilen im Code enthalten Kommentare, die beschreiben, was sie tun. Grundsätzlich wird mit dieser Option, wenn R auf einen stop()auslösenden Fehler stößt , eine RData (* .rda) -Dump-Datei des Arbeitsspeichers in allen aktiven Umgebungen im Verzeichnis erstellt ~/myUsername/directoryForDumpund ein Fehlerprotokoll error.logmit einigen nützlichen Informationen in das Verzeichnis geschrieben gleiches Verzeichnis. Sie können dieses Snippet ändern, um eine andere Fehlerbehandlung hinzuzufügen (z. B. einen Zeitstempel zur Speicherauszugsdatei und zu den Dateinamen des Fehlerprotokolls usw. hinzufügen).

    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. Stellen Sie sicher, dass aus dem Hauptskript und allen nachfolgenden Funktionsaufrufen die Option keep.source=TRUEverwendet wird , wenn eine Funktion bezogen wird. Das heißt, um eine Funktion zu beschaffen, würden Sie verwenden source('~/path/to/myFunction.R', keep.source=TRUE). Dies ist erforderlich, damit die traceback()Ausgabe Zeilennummern enthält. Es sieht so aus, als könnten Sie diese Option möglicherweise auch global festlegen options( keep.source=TRUE ), aber ich habe dies nicht getestet, um festzustellen, ob es funktioniert. Wenn Sie keine Zeilennummern benötigen, können Sie diese Option weglassen.

  3. Rufen Sie vom Terminal (außerhalb von R) das Hauptskript im Batch-Modus mit auf Rscript myMainScript.R. Dadurch wird eine neue nicht interaktive R-Sitzung gestartet und das Skript ausgeführt myMainScript.R. Das in Schritt 1 angegebene Code-Snippet oben in myMainScript.Rlegt die Fehlerbehandlungsoption für die nicht interaktive R-Sitzung fest.
  4. Stoßen Sie irgendwo in der Ausführung von auf einen Fehler myMainScript.R. Dies kann im Hauptskript selbst sein oder mehrere Funktionen tief verschachteln. Wenn der Fehler auftritt, wird die Behandlung wie in Schritt 1 angegeben ausgeführt und die R-Sitzung wird beendet.
  5. Eine RData-Dump-Datei mit dem Namen errorDump.rdaund ein Fehlerprotokoll mit dem Namen error.logwerden in dem Verzeichnis erstellt, das in der '~/myUsername/directoryForDump'Einstellung für die globale Fehlerbehandlungsoption angegeben ist .
  6. In Ihrer Freizeit inspizieren error.logInformationen über den Fehler zu überprüfen, einschließlich der Fehlermeldung selbst und der vollständigen Stack - Trace zu dem Fehler führt. Hier ist ein Beispiel für das Protokoll, das bei einem Fehler generiert wurde. Beachten Sie, dass die Zahlen nach dem #Zeichen die Zeilennummern des Fehlers an verschiedenen Stellen im Aufrufstapel sind:

    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. Sie können nach Belieben mit errorDump.rdain eine interaktive R-Sitzung laden load('~/path/to/errorDump.rda'). Rufen Sie debugger(errorDump)nach dem Laden auf, um alle R-Objekte im Speicher in einer der aktiven Umgebungen zu durchsuchen. debugger()Weitere Informationen finden Sie in der R-Hilfe .

Dieser Workflow ist enorm hilfreich, wenn Sie R in einer Produktionsumgebung ausführen, in der nicht interaktive R-Sitzungen über die Befehlszeile initiiert werden und Informationen über unerwartete Fehler beibehalten werden sollen. Die Möglichkeit, Speicher in eine Datei zu kopieren, mit der Sie den Arbeitsspeicher zum Zeitpunkt des Fehlers überprüfen können, sowie die Zeilennummern des Fehlers im Aufrufstapel ermöglichen ein schnelles Post-Mortem-Debugging der Fehlerursache.

bmosov01
quelle
0

Erst options(show.error.locations = TRUE)und dann traceback(). Die Fehlerzeilennummer wird nach # angezeigt

den2042
quelle