【问题标题】:Determine whether a file is in use in Perl on Windows确定文件是否在 Windows 上的 Perl 中使用
【发布时间】:2011-03-14 07:24:18
【问题描述】:

我正在编写一些 Perl,它获取录制在 Windows Media Center 上的电视节目并根据特定条件移动/重命名/删除它们。

由于 Perl 运行相当频繁,我想清楚地确定文件是否正在使用(换句话说,节目正在录制过程中),这样我就可以避免对它做任何事情。

我当前的方法查看文件的状态(使用“stat”)并在 5 秒后再次比较,如下所示:

sub file_in_use
{
  my $file = shift;

  my @before = stat($file);
  sleep 5;
  my @after = stat($file);

  return 0 if ($before ~~ $after);
  return 1;
 }

这似乎可行,但我意识到可能有更好、更清洁的方法来做到这一点。

你能建议吗?

【问题讨论】:

  • 这是一个很好的方法,我不相信你会找到很多选择......另一种方法是读取应用程序 pid 并在 pid 有后移动文件走了……
  • 来自 Prix(我认为指出这一点很重要):如果文件正被全时使用且未释放,则无法对文件执行移动 [或删除],因为它会产生错误...。 (根据不同的文件模式设置,副本可能会起作用。)即使使用 stat 方法,您也必须仍然处理 FS 场景并仅将 file_in_use 用作“提示”(尽管可能竞争条件可能永远不会在这里实现)。
  • @pst 是的,这就是为什么我认为 move 是一个很好的方法......因为如果文件仍然被锁定,它将返回 0 或告诉你一个错误,具体取决于你的编码方式你可以继续尝试,直到它被释放……
  • 好点。我刚刚在我的原始逻辑中发现了一个致命缺陷,结果是“stat”的结果在录制过程中不会改变。所以我可以检查文件是否被锁定的唯一方法是重命名它并检查返回码。
  • 实际上是因为文件正在被写入和锁定,而不是动态写入,它只创建并打开文件,记录完成后,关闭文件并更新数据文件。

标签: windows perl file winapi testing


【解决方案1】:

我建议的唯一改进是一次 stat 你的所有文件,因此你只需要一次休眠 5 秒,而不是为每个文件休眠 5 秒:

my (%before, %after);
foreach my $file (@files_that_might_be_in_use) {
    $before{$file} = [ stat $file ];
}
sleep 5;
foreach my $file (@files_that_might_be_in_use) {
    $after{$file} = [ stat $file ];

    if ( $before{$file} ~~ $after{$file} ) {
        # file is not in use ... 
    } else {
        # file is in use ... 
    }
}

【讨论】:

    【解决方案2】:

    如果录制过程锁定了文件,您可以尝试以读写模式打开它,看看它是否失败,ERROR_SHARING_VIOLATIONGetLastError(通过 Perl 的 $^E 特殊变量访问)。

    例如:

    #! /usr/bin/perl
    
    use warnings;
    use strict;
    
    sub usage { "Usage: $0 file ..\n" }
    
    die usage unless @ARGV;
    
    foreach my $path (@ARGV) {
      print "$path: ";
    
      if (open my $fh, "+<", $path) {
        print "available\n";
        close $fh;
      }
      else {
        print $^E == 0x20 ? "in use by another process\n" : "$!\n";
      }
    }
    

    Adobe 阅读器打开 Dir100526Lt.pdf 的示例输出:

    C:\Users\Greg\Downloads>check-lock.pl Dir100526Lt.pdf setup.exe
    Dir100526Lt.pdf:正在被另一个进程使用
    setup.exe:可用

    请注意,任何时候您首先测试一个条件,然后根据该测试的结果采取行动,您就是在创建一个race condition。似乎在您的应用程序中这可能会影响您的最糟糕的情况是以下不幸的顺序:

    1. 如上所述测试视频的可用性
    2. 回答:可用!
    3. 与此同时,录像机启动并锁定视频
    4. 回到您的程序中,您尝试移动视频,但由于共享冲突而失败

    【讨论】:

    • 完美!非常感谢。关于比赛条件的好点 - 因此,我希望,一旦录制完成,就没有理由再次启动它。
    • cookie for you 我喜欢那个特殊变量...只是一个简单的问题,如果他知道他需要检查的文件并检查它当时是否可用并立即锁定它会是就像毫秒一样,直到另一个应用程序可以控制它,所以它会非常非常非常难发生吗?
    • @Prix 毫秒对于计算机来说是很长的时间。不太可能意味着它有时会发生,并且这类错误往往难以调试。当然,在这种情况下,可能不值得大惊小怪,但训练自己发现同步裂缝对我们有好处。与其检查可用性然后锁定,不如立即获取锁定。如果其他人拥有它,系统会在它可用时立即将其提供给您。否则,您知道自己拥有它,并且其他人将无法使用它。
    • 0x20 是否记录在某处?你是怎么得出这个数字的?
    • @RenéNyffenegger 见ERROR_SHARING_VIOLATIONSystem Error Codes on MSDN
    【解决方案3】:

    存在一个 Windows API,它将列出保持文件打开的进程数。它是Restart Manager (Rststmgr.dll)。基于 artical How do I find out which process has a file open?,在 Win32::API 的帮助下,您可以将程序转换为 Perl 程序,该程序告诉您打开文件的进程数,甚至列出进程 ID。

    每个测试应该是 RmStartSession、RmRegisterResources、RmGetList、RmEndSession 的序列。使用encode("UTF-16LE",$filename) 制作CWSTR 类型的字符串。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-11-23
      • 2013-04-13
      • 2012-11-16
      • 2013-12-12
      • 2015-06-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多