如何检查文件是否已在 tcl 中打开

Pun*_*tal 1 file-io tcl

我有一个 tcl 程序,在其中打开一个文件,如果不存在则创建用于写入。现在我正在其中记录一些 put 语句以进行调试。现在,在此过程结束时,我关闭该文件。但在中间我正在调用另一个过程,我也需要在该过程中的这个打开的文件中写入一些内容。所以我想做这样的事情:

proc ::myproc {args} { 
  set fp [open "C:\\log.txt" w+];
  puts $fp "Checkpoint 1";
  set retVal [::myprocII];
  puts $fp "Checkpoint 2";
  close $fp;
  return 1;
}

proc ::myprocII {} {
  set fp [open "C:\\log.txt" w+];
  puts $fp "Checkpoint 3";
  close $fp;
  return 1;
}
Run Code Online (Sandbox Code Playgroud)

那么,当我在 myprocII 中打开同一文件并记录数据并关闭它时,这不是导致错误或异常的原因吗?然后,即使在关闭 myprocII 中的文件后,我仍在调用 proc myproc 中记录数据。我尝试对此进行测试,但由于我是从批处理文件运行它,因此在我弄清楚错误是什么之前窗口会关闭。

所以我想知道这是否正确,或者如果不正确,我如何继续将来自不同程序的数据附加到同一日志文件中。

Joh*_*uhn 5

有以下几种选择:

  • 使用 1 个文件描述符,在程序末尾关闭它。

    proc log {data} {
        global logfd
        if {![info exists logfd] || $logfd == ""} {
            set logfd [open {C:\log.txt} w]
        }
        puts $logfd $data
    }
    
    # before you exit, close it:
    catch {close $::logfd}
    
    Run Code Online (Sandbox Code Playgroud)

    当程序终止时,Tcl 应该在退出时自行关闭文件。

  • 每次写入时打开/关闭文件。仅在附加模式下有用

    proc log {data} {
        set fd [open {C:\log.txt} a]
        catch {
            puts $fd $data
        } res opt
        close $fd
        return -options $opt $res
    }
    
    Run Code Online (Sandbox Code Playgroud)

    这不是性能最好的解决方案,但它很干净。

  • 使用一些技巧

    rename open _open
    rename close _close
    proc open {path args} {
       global sharedfd
       if {$path eq {C:\log.txt}} {
           if {[info exists sharedfd] && [dict exists $sharedfd fd]} {
               dict incr sharedfd refcount
               return [dict get $sharedfd fd]
           } else {
               set fd [_open $path {*}$args]
               dict set sharedfd fd $fd
               dict set sharedfd refcount 1
               return $fd
           }
       }
       return [_open $path {*}$args]
    }
    
    proc close {fd args} {
        global sharedfd
        if {[info exists sharedfd] 
                && [dict exists $sharedfd fd] 
                && [dict get $sharedfd fd] eq $fd} {
            dict incr sharedfd refcount -1
            if {[dict get $sharedfd refcount] <= 0} {
               _close $fd
               unset sharedfd
            }
            return
        }
        _close $fd {*}$args
    }
    
    Run Code Online (Sandbox Code Playgroud)

    C:\log.txt如果已经打开,则返回相同的 fd ,保留引用计数器,当引用计数为 0 时关闭通道
    。请注意,这是一个 hack。您可能不应该修改标准命令。

  • 日志应该_始终_以附加模式写入。在正确的 POSIX 系统上,这使得操作系统确保写入不会重叠,即使日志是从多个进程写入的。 (2认同)