Tar 个文件在单独的进程中使用 open 和 vwait

Tar files in separate process using open and vwait

我有一个内置于 Tcl/Tk 中的 GUI,它有一个可以 tar 向上目录的按钮。 该目录可能非常大,所以我不想在等待时锁定 GUI。

为了实现这一点,我对命令管道和 vwait 使用了 open,但是当 tar 为 运行 时,GUI 仍然没有响应。这是我的代码:

set ::compress_result 0
set pipe [open "|$tar_executable -cf $folder_to_tar.tar $folder_to_tar" r+]
fileevent $pipe readable "set ::compress_result [gets $pipe line]"

vwait ::compress_result
set return_value $::compress_result
unset ::compress_result
close $pipe

为什么这仍然会阻塞 Tcl 事件循环并锁定 GUI?

正在添加

fconfigure $pipe -blocking false

打开管道后会有帮助。

你遇到的关键问题是这一行:

fileevent $pipe readable "set ::compress_result [gets $pipe line]"

立即从管道中读取一行 因为 [gets …] 在双引号字符串中。更改为:

fileevent $pipe readable {set ::compress_result [gets $pipe line]}

使事情正常进行,因为它推迟了管道的读取,直到管道变得可读。但是,要做到这一点,它依赖于 pipe 变量是全局的。实际上这样做更好:

fileevent $pipe readable [list apply {pipe {
    global compress_result
    set compress_result [gets $pipe line]
}} $pipe]

这非常丑陋且难以调试,所以我们 实际上 使用辅助程序:

proc pipedone {pipe} {
    global compress_result
    set compress_result [gets $pipe line]
}
fileevent $pipe readable [list pipedone $pipe]

此处使用 list 确实“将此作为可运行的脚本引用以备后用”,处理您可能在变量中遇到的任何意外技巧。它知道如何正确引用事物,因此您不必这样做。


在 Tcl 8.6 中,您最好使用协程。

coroutine piperead apply {{tar folder} {
    # Open the pipe
    set pipe [open |[list $tar -cf $folder.tar $folder] "r"]
    # Wait until readable
    fileevent $pipe readable [info coroutine]
    yield
    # Read and close
    set return_result [gets $pipe line]
    close $pipe
    return $return_result
}} $tar_executable $folder_to_tar