如何(暂时)捕获标准输出
How to (temporarily) capture 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
目前的努力
目前我可以使用临时文件捕获标准输出,但无法释放回标准输出。我用以下方法做到这一点:
template captureStdout*(ident: untyped, body: untyped) =
discard reopen(stdout, tmpFile, fmWrite)
body
ident = readFile(tmpFile)
使用此 main.nim
运行时没有断言错误,但输出仅为
hello1
在 tmpFile 中我看到:
hello2
ciao3
当您调用 reopen
时,您将变量 stdout
重新分配给写入 tmpFile
.
的 File
为了将输出打印到系统 STDOUT,您需要将变量 stdout
重新分配给写入系统 STDOUT 的 File
。
因此,Linux 和 Windows 的答案不同。
对于Linux,这样做的方法是使用dup 和dup2 C 函数来复制stdout
文件描述符并使用不同的文件(因此您可以恢复stdout)。
由于 dup 和 dup2 不在 Nim 的 system/io
中,我们需要绑定到 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()
如何在 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
目前的努力
目前我可以使用临时文件捕获标准输出,但无法释放回标准输出。我用以下方法做到这一点:
template captureStdout*(ident: untyped, body: untyped) =
discard reopen(stdout, tmpFile, fmWrite)
body
ident = readFile(tmpFile)
使用此 main.nim
运行时没有断言错误,但输出仅为
hello1
在 tmpFile 中我看到:
hello2
ciao3
当您调用 reopen
时,您将变量 stdout
重新分配给写入 tmpFile
.
File
为了将输出打印到系统 STDOUT,您需要将变量 stdout
重新分配给写入系统 STDOUT 的 File
。
因此,Linux 和 Windows 的答案不同。
对于Linux,这样做的方法是使用dup 和dup2 C 函数来复制stdout
文件描述符并使用不同的文件(因此您可以恢复stdout)。
由于 dup 和 dup2 不在 Nim 的 system/io
中,我们需要绑定到 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()