วิธีจับภาพ stdout (ชั่วคราว)

Sep 23 2020

ฉันจะจับ Stdout ชั่วคราวใน Nim ได้อย่างไร

ฉันต้องการเทมเพลตที่มีลายเซ็นดังต่อไปนี้:

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 ระบบคุณจะต้องมอบหมายตัวแปรstdoutกับFileที่เขียนไป STDOUT Systel ของคุณ

ดังนั้นคำตอบจึงแตกต่างกันระหว่าง Linux และ Windows

สำหรับ Linux วิธีการทำคือใช้ฟังก์ชัน dup และ dup2 C เพื่อทำซ้ำstdoutfile descriptor และใช้ไฟล์อื่น (เพื่อให้คุณสามารถกู้คืน stdout ได้) ตั้งแต่ DUP และ dup2 ไม่ได้อยู่ในsystem/ioใน Nim 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()