stdout을 (일시적으로) 캡처하는 방법

Sep 23 2020

Nim에서 stdout을 임시로 캡처하려면 어떻게합니까?

다음 서명이있는 템플릿을 갖고 싶습니다.

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

이 코드 ( main.nim)는 오류없이 실행됩니다.

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

출력은 다음과 같아야합니다.

hello1
ciao3

현재의 노력

현재 임시 파일을 사용하여 stdout을 캡처 할 수 있지만 stdout으로 다시 릴리스 할 수 없습니다. 다음과 같이합니다.

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

이것은 main.nim어설 션 오류없이 실행되지만 출력은

hello1

tmpFile에서 다음을 참조하십시오.

hello2
ciao3

답변

3 Clonk Sep 23 2020 at 16:16

호출 reopen할 때에 쓰는에 변수 stdout를 다시 할당합니다 .FiletmpFile

출력을 시스템 STDOUT에 인쇄하려면 systel STDOUT에 쓰는 변수 stdout를 a 에 다시 할당해야합니다 File.

따라서 대답은 Linux와 Windows간에 다릅니다.

Linux의 경우이를 수행하는 방법은 dup 및 dup2 C 함수를 stdout사용 하여 파일 설명자를 복제하고 다른 파일을 사용하는 것입니다 (따라서 stdout을 복원 할 수 있음). dup과 dup2는 system/ioNim 에 없기 때문에 unistd.h.

예를 들면 다음과 같습니다.

#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()