【问题标题】:Tcl 'file exists' misses generated filesTcl 'file exists' 错过了生成的文件
【发布时间】:2014-07-03 19:37:14
【问题描述】:

在调试第三方 Tcl 脚本时,我遇到了一个非常奇怪的行为:当脚本通过 gunzip 解压缩现有文件时,Tcl 'file exists' 否认结果文件存在,即使它通过 'ls 可见' 从 shell 甚至通过 Tcl 'glob'。我能够在这个简短的(ish)脚本中复制行为:

#!/usr/bin/tclsh

proc assert {msg asserted} {
  if {!$asserted} {
    puts "Assertion failed: $msg"
    exit 1
  }
}

set script_path [info script]
set test_file [file join [file dirname $script_path] "test.tcl.gz"]
puts "The test file is '$test_file'"
assert "The test file does not exist" [file exists $test_file]

set this_dir [pwd]
set test_dir [file join $this_dir "test"]
if {![file exist $test_dir]} {
  file mkdir $test_dir
}
puts "The test directory is '$test_dir'"
assert "'$test_dir' does not exist or is not a directory" [file isdirectory $test_dir]

file copy $test_file $test_dir
set working_file [file join $test_dir [file tail $test_file]]
assert "Failed to to copy the test file to '$working_file'" [file exists $working_file]

exec gunzip $working_file
puts "After decompression, $test_dir contains:"
foreach file [glob -dir $test_dir *] {
  puts $file
}
set decompressed_file [file rootname $working_file]
assert "$decompressed_file does not exist" [file exists $decompressed_file]

我使用的测试文件只是脚本源的 gzip 副本。正如脚本所期望和检查的那样,它与脚本本身位于同一目录中。这是我运行它时得到的结果(tcl 8.5 / CentOS 6.5):

bash-4.1$ ./test.tcl 
The test file is './test.tcl.gz'
The test directory is '/home/jbolling/tmp/test'
After decompression, /home/jbolling/tmp/test contains:
/home/jbolling/tmp/test/test.tcl
Assertion failed: /home/jbolling/tmp/test/test.tcl does not exist

谁能解释这种行为?有没有办法说服“文件存在”识别解压文件?

【问题讨论】:

  • 我在运行脚本时没有收到断言失败错误。你能做一个ls -ld . test test/test.tcl
  • 我也没有看到断言失败。我的环境:Mac OS X 10.9.x,Tcl 8.5.9。
  • file exists 命令几乎直接映射到所有 Unix 系统(包括 OSX)上带有 F_OK 标志的 access() 系统调用;由于文件锁定模型的不同,Windows 更加复杂。你在哪个平台上?
  • @Brad Lanam: 'ls' 显示文件存在,就像 'glob' 一样。工作目录 (. = /home/jbolling/tmp) 的权限为 0750,子目录“test”的权限为 0755,test/test.tcl 的权限为 0644。
  • @Donal Fellows:这是 CentOS 6.5 (Linux)。

标签: tcl


【解决方案1】:

file exists 的手册指出该命令

如果文件名存在且当前用户有搜索,则返回 1 通向它的目录的权限,否则为 0。

显然该文件存在,所以我怀疑您运行脚本的用户没有查看它的权限。我必须承认我不太确定“指向它的目录的搜索权限”是什么意思。

我建议使用位于可公开访问目录中的文件再次尝试。

【讨论】:

  • /a/b/c/d/file.txt 的“搜索权限”意味着您必须能够 cd 进入 /a/a/b/a/b/c ... 等等,一直到 /a/b/c/d
  • 脚本创建目标文件,首先将压缩后的版本复制到目标目录,然后解压缩。它以我的身份运行,我当然对它的路径中的所有目录(我的主目录的子目录)拥有搜索权限,我可以在脚本之外进行验证。
  • 另外,该脚本在解压前成功使用file exists测试了目标目录中.gz的存在,所以该问题无法解释为对该目录的搜索权限不足。
  • 很奇怪。我想知道它是否是您将其作为 shell 脚本运行的人工制品。你能打开一个TCL shell,直接把代码粘贴进去,看看有什么不同吗?
  • 我打开了一个交互式 tclsh,并将我的原始脚本(上图)粘贴到其中。它给了我同样的“glob”结果和同样不一致的断言失败。这是一个有趣的想法,但不是骰子。
【解决方案2】:

从其他人的 cmets 得知原始脚本对他们有效,发现了一些对我也有效的变体,并在 Tcl 源代码中四处寻找后,我得出了一个结论:曾经使用过 file exists on一个对象,如果目录的内容可能在调用之间发生了变化(通常不能排除),那么在该对象或通过file rootname从它派生的对象上再次使用它是不安全的。

我还没有追查到它在源代码中发生的位置,但是我的 Tcl 解释器显然在某处缓存目录列表的结果,因为它不仅无法识别解压缩文件的创建,它也无法识别删除原件(gunzip 也执行)。另一方面,如果我使用文字字符串路径或使用与之前传递给file exists 的任何对象充分分离的Tcl 对象执行file exists 测试,则结果符合预期。如果我通过例如替换来强制转换为字符串,它也可以工作

assert "$decompressed_file does not exist" [file exists $decompressed_file]

assert "$decompressed_file does not exist" [file exists [string trim $decompressed_file]]

显然缓存行为是特定于实现的;我的实现是 CentOS 6.5 的 Tcl 8.5.7,但我认为这种行为至少对于 Tcl 8.5.7 的 Unix 实现可能是通用的(这与 macosx 实现不同,即使 OS X 是 Unix)。也可能是 gunzip 做了一些不寻常的事情导致了这个问题,因为我无法通过 execing 一个简单的 mv 命令来复制它。

无论如何,即使我还没有完全描述问题,我认为我现在已经足够了解它,可以有信心解决这个问题。感谢那些发表评论的人——你们引导我走向富有成果的方向。

【讨论】:

    【解决方案3】:

    小问题,但这里看起来可能是错字...

    if {![file exist $test_dir]} {
    

    应该是:

    if {![file exists $test_dir]} {
    

    【讨论】:

    • 感谢您的评论。 (1)您是正确的,发布的源代码中有错字,但它实际上并没有以任何方式(奇怪)影响脚本的行为。该脚本完全按照最初发布的方式运行,并且它也可以在纠正错字的情况下运行,从而产生完全相同的文件系统更改和打印输出。 (2) 这比作为答案更适合作为对原始问题的评论,因为它实际上并没有回答问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-05-08
    • 1970-01-01
    • 1970-01-01
    • 2022-12-26
    • 1970-01-01
    • 2017-12-25
    相关资源
    最近更新 更多