Wie man (vorübergehend) stdout erfasst

Sep 23 2020

Wie kann ich stdout in Nim vorübergehend erfassen?

Ich hätte gerne eine Vorlage mit folgender Signatur:

template captureStdout(ident: untyped, body: untyped) = discard

So dass dieser Code ( main.nim) fehlerfrei ausgeführt wird:

var msg = "hello"
echo msg & "1"
var s: string
captureStdout(s):
  echo msg & "2"
  msg = "ciao"
echo msg & "3"
assert s == "hello2\n"

und die Ausgabe sollte sein:

hello1
ciao3

aktuelle Bemühungen

Derzeit kann ich stdout mit einer temporären Datei erfassen, aber ich kann es nicht wieder an stdout freigeben. Ich mache das mit folgendem:

template captureStdout*(ident: untyped, body: untyped) =
  discard reopen(stdout, tmpFile, fmWrite)
  body
  ident = readFile(tmpFile)

Dies main.nimläuft ohne Assertionsfehler, aber die Ausgabe erfolgt nur

hello1

und in tmpFile sehe ich:

hello2
ciao3

Antworten

3 Clonk Sep 23 2020 at 16:16

Wenn Sie aufrufen reopen, weisen Sie die Variable stdouteiner Variablen zu File, in die geschrieben wird tmpFile.

Um die Ausgabe an das System STDOUT zu drucken, müssen Sie die Variable stdouteiner Variablen zuweisen, die in Ihr System STDOUT Fileschreibt.

Daher unterscheiden sich die Antworten zwischen Linux und Windows.

Unter Linux besteht die Möglichkeit darin, den Dateideskriptor mit den Funktionen dup und dup2 C zu duplizieren stdoutund eine andere Datei zu verwenden (damit Sie stdout wiederherstellen können). Da dup und dup2 nicht in system/ioNim enthalten sind, benötigen wir Bindungen zu unistd.h.

Hier ist ein Beispiel:

#Create dup handles
proc dup(oldfd: FileHandle): FileHandle {.importc, header: "unistd.h".}
proc dup2(oldfd: FileHandle, newfd: FileHandle): cint {.importc,
    header: "unistd.h".}

# Dummy filename
let tmpFileName = "tmpFile.txt"

template captureStdout*(ident: untyped, body: untyped) =
  var stdout_fileno = stdout.getFileHandle()
  # Duplicate stoud_fileno
  var stdout_dupfd = dup(stdout_fileno)
  echo stdout_dupfd
  # Create a new file
  # You can use append strategy if you'd like
  var tmp_file: File = open(tmpFileName, fmWrite)
  # Get the FileHandle (the file descriptor) of your file
  var tmp_file_fd: FileHandle = tmp_file.getFileHandle()
  # dup2 tmp_file_fd to stdout_fileno -> writing to stdout_fileno now writes to tmp_file
  discard dup2(tmp_file_fd, stdout_fileno)
  #
  body
  # Force flush
  tmp_file.flushFile()
  # Close tmp
  tmp_file.close()
  # Read tmp
  ident = readFile(tmpFileName)
  # Restore stdout
  discard dup2(stdout_dupfd, stdout_fileno)

proc main() =
  var msg = "hello"
  echo msg & "1"
  var s: string

  captureStdout(s):
    echo msg & "2"
    msg = "ciao"

  echo msg & "3"
  echo ">> ", s
  assert s == "hello2\n"

when isMainModule:
  main()
  # Check it works twice
  main()