我想检查两个文件句柄是否引用同一个文件。为此,我可以使用应用于每个文件句柄的stat
函数吗?
预先感谢
my $file = 'C:\temp\file.txt');
open( TXT1, "> $file" );
open( TXT2, "> $file" );
print( "The handles refer to the same file!") if (\TXT1 eq \TXT2);
答案 0 :(得分:4)
通常不能(便携式地)做到这一点,†可以理解,因为“文件”句柄根本不需要与文件关联。您可以做的一件事是记录每个文件句柄的fileno。
所以打开文件时
my %filename_fileno;
open my $fh, '>', $file or die "Can't open $file: $!";
$filename_fileno{fileno $fh} = $file;
然后您可以在需要时查找它‡
say "Filename is: ", $filename_fileno{fileno $fh};
当该文件被关闭时,别忘了从哈希中删除条目
delete $filename_fileno{fileno $fh};
close $fh;
因此,这些应位于实用程序函数中。如‡脚注中所述,由于需要更多的照顾,所以总的来说这将构成一个不错的小模块。然后,您还可以考虑扩展(继承自)相关的模块,例如Path::Tiny。
注意:您不能从这样的单独文件句柄写入文件。每个文件句柄上的操作都会跟踪 文件句柄在文件中的最后位置,因此写入将破坏另一个文件句柄的中间写入。
注意:使用词法文件句柄(my $fh
)而不是glob(FH
),使用三参数open
,并且始终检查open
调用。
†在某些(大多数?)Linux系统上,您可以使用/proc
文件系统
say readlink("/proc/$$/fd/" . fileno $fh);
以及更多(全部?)Unix-y系统可以使用(设备和)索引节点编号
say for (stat $fh)[0,1];
‡可以使用软链接(符号链接)和硬链接来更改数据,并使用不同的名称。因此,我们可以使用不同的文件名,但使用相同的“文件”(数据)。
在Windows系统上,this post中给出了最好的检查方法,但据我所知,硬链接必须使用另一个答案的方法(解析输出)。
此外,非规范名称以及大小写不同(在不区分大小写的系统上),某些系统上的短/长名称(更多?)...可以为同一文件指定不同的名称。使用模块可以更轻松地进行清理,但是还需要添加它。
在大多数(所有其他)系统上,inode的概念以及任何可用的类似stat
的功能都使这些问题不再存在,因为device + inode唯一地引用数据。
感谢ikegami对此发表评论。
答案 1 :(得分:2)
是的,在某些系统+设备组合中,可以使用stat
。
use File::stat;
my $st1 = stat($fh1)
or die $!;
my $st2 = stat($fh2)
or die $!;
say $st1->dev == $st2->dev && $st1->ino == $st2->ino ? "same" : "different";
值得注意的是,这不适用于您似乎正在使用的NTFS或FAT32。
您应该让自己的程序保持跟踪,但这说起来容易做起来难。当您必须与硬链接,软链接,带有.
/ ..
的路径,大写/斜杠差异,短/长名称,常规名称竞争时,很难确定两个路径是否引用同一个文件/ UNC路径,共享等。
例如,以下所有路径都可以引用同一文件:
C:\Moo\Foo Bar.txt
C:\Hardlink\Foo Bar.txt
C:\Softlink\Foo Bar.txt
C:\.\Moo\Foo Bar.txt
C:\Baz\..\Moo\Foo Bar.txt
c:\moo\foo bar.txt
C:/Moo/Foo Bar.txt
C:\Moo\FOOBAR~1.TXT
\\?\C:\Moo\Foo Bar.txt
\\127.0.0.1\C$\Moo\Foo Bar.txt
Z:\Moo\Foo Bar.txt
除最后两个以外的所有内容都可以通过规范化处理。