TCL中的FIFO文件

4
我需要在一个命名管道中解压文件并将其返回:
proc unzip_file_if_needed { fileName } {
    if { [file extension $fileName] != ".gz" } {
        return $fileName;
    }
    set tmpDir [fileutil::tempdir]
    set tmpFileName [ file join $tmpDir [ pid ] ]
    if { [file exists $tmpFileName ] } {
        file delete $tmpFileName
    }
    exec mkfifo $tmpFileName
    exec gunzip -c $fileName > $tmpFileName &
    return $tmpFileName
}

它卡在了 exec gunzip -c $fileName > $tmpFileName & 上面


根据mkfifo手册,这种文件需要同时打开以进行读写。这意味着有人需要读取您解压缩到其中的数据。有人会读取您解压缩到mkfifo文件中的内容吗? - Bogdan
是的,它将在函数调用后被读取,这就是为什么gunzip在后台执行的原因。 - Armen Yeganyan
2个回答

3
问题在于内核会在open()系统调用中阻塞,直到 fifo 为相反方向打开,而 Tcl 在分叉之前在父进程中创建重定向(因为这样可以在正常情况下提供更可靠的错误处理)。你需要传递O_NONBLOCK标志到open()系统调用中,但是exec命令无法控制它。因此需要进行一些诡计!
set fd [open $tmpFileName {WRONLY NONBLOCK}]
exec gunzip -c $fileName >@$fd &
close $fd

这是通过手动使用我们想要的标志进行open,然后将该描述符传递给子进程来实现的。请注意,由于这是我们正在设置的管道的写入端,因此我们必须以WRONLY模式打开(这在某种程度上类似于open...w在内部执行的操作,减去这里不适用的一些标志,再加上我们想要的NONBLOCK魔法)。


Donal Fellows 报告了一个错误:无法打开“/tmp/52977”:没有这样的设备或地址。 - Armen Yeganyan

0

我用以下方法解决了这个问题:

proc unzip_file_if_needed { fileName } {
    if { [file extension $fileName] != ".gz" } {
        return $fileName;
    }
    set tmpDir [fileutil::tempdir]
    set pId [pid]
    set tmpFileName [ file join $tmpDir pId ]
    set unzipCmd [ file join $tmpDir [ append pId "cmd.sh" ] ]
    if { [file exists $tmpFileName ] } {
        file delete $tmpFileName
    }

    if { [file exists $unzipCmd ] } {
        file delete $unzipCmd
    }

    set cmdDesc [open $unzipCmd { CREAT EXCL RDWR} 0777]
    puts $cmdDesc "\#!\/bin\/bash\n gunzip -c \$1 > \$2"
    close $cmdDesc

    exec mkfifo $tmpFileName
    exec $unzipCmd $fileName $tmpFileName >&@1 &

    return $tmpFileName
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接