对于 FOR 循环中的目录,“%~zI” 究竟扩展成什么?

What exactly is “%~zI” expanded to for directories in FOR loops?

来自FOR /?

In addition, substitution of FOR variable references has been enhanced.
You can now use the following optional syntax:

    %~I         - expands %I removing any surrounding quotes (")
    %~fI        - expands %I to a fully qualified path name
    %~dI        - expands %I to a drive letter only
    %~pI        - expands %I to a path only
    %~nI        - expands %I to a file name only
    %~xI        - expands %I to a file extension only
    %~sI        - expanded path contains short names only
    %~aI        - expands %I to file attributes of file
    %~tI        - expands %I to date/time of file
    %~zI        - expands %I to size of file
    %~$PATH:I   - searches the directories listed in the PATH
                   environment variable and expands %I to the
                   fully qualified name of the first one found.
                   If the environment variable name is not
                   defined or the file is not found by the
                   search, then this modifier expands to the
                   empty string

我有 运行 一个 Windows 批处理脚本,它在 FOR 循环中执行 @echo %~aI %~fI ^<%~zI byte^(s^)^> 循环遍历目录(每个目录的路径都存储在 %I) 并得到这个输出:

d--hs------ J:$RECYCLE.BIN  <0 byte(s)>
d---------- J:\Multimedia  <4096 byte(s)>
dr--------- J:\-C-\……\Desktop  <12288 byte(s)>
dr--------- J:\-C-\……\Documents  <28672 byte(s)>
dr--------- J:\-C-\……\Downloads  <81920 byte(s)>

上述目录的“大小”与其中文件的大小无关。来自 %~zI 的“大小”到底是什么意思?如果 %I 是一个普通文件,它就是它的大小。但是如果 %I 是一个目录呢?我不太明白。真的没有意义吗?

这是目录条目

消耗的space

目录实际上是一个包含其他文件和目录的特殊文件,因此它必须将该列表与必要的元数据一起存储在某个地方。一些文件系统会分配正常的簇并将元数据存储在该数据区域

NTFS 将对大文件夹执行相同的操作。但是,在 NTFS 中,小文件也可以 stay resident in the MFT entry,这就是为什么您可以看到一些零字节文件夹的原因,因为它们不需要为目录元数据单独分配块

包含这些元数据的流的名称是 $I30

In the case of directories, there is no default data stream, but there is a default directory stream. Directories are the stream type $INDEX_ALLOCATION. The default stream name for the type $INDEX_ALLOCATION (a directory stream) is $I30

5.1 NTFS Streams

您可以使用 fsutil file layout <directory_path> 检查并查看 $I30 流。例如,这里是我的 PC 的输出。请注意 %~zIfsutil 输出中的相同大小。大小为 0 的文件夹只包含一个很小的 ​​$INDEX_ROOT 流,而其他文件夹有另一个 $INDEX_ALLOCATION,其大小与 %~zI

的输出相同
PS C:\> cmd /c "for /d %I in (*) do @echo %~aI %~fI  ^<%~zI byte^(s^)^>"
d---------- C:\ESD  <0 byte(s)>
d---------- C:\Intel  <0 byte(s)>
d---------- C:\PerfLogs  <0 byte(s)>
dr--------- C:\Program Files  <8192 byte(s)>
dr--------- C:\Program Files (x86)  <4096 byte(s)>
dr--------- C:\Users  <4096 byte(s)>
d---------- C:\Windows  <16384 byte(s)>
d---------- C:\Windows.old  <4096 byte(s)>

PS C:\> foreach ($f in ls -Attr Directory) {
>>     $fileLayout = (fsutil file layout $f) -join "`0"
>>     $result = (([regex]'$I30.*?(?=Stream|$)').Matches($fileLayout)) -split "`0" | Select-String -Pattern '$I30|  Size'
>>     echo "================================ $f"; $result
>> }
================================ ESD

$I30:$INDEX_ROOT
    Size                : 48
================================ Intel
$I30:$INDEX_ROOT
    Size                : 368
================================ PerfLogs
$I30:$INDEX_ROOT
    Size                : 48
================================ Program Files
$I30:$INDEX_ROOT
    Size                : 168
$I30:$INDEX_ALLOCATION
    Size                : 8,192
$I30:$BITMAP
    Size                : 8
================================ Program Files (x86)
$I30:$INDEX_ROOT
    Size                : 56
$I30:$INDEX_ALLOCATION
    Size                : 4,096
$I30:$BITMAP
    Size                : 8
================================ Users
$I30:$INDEX_ROOT
    Size                : 56
$I30:$INDEX_ALLOCATION
    Size                : 4,096
$I30:$BITMAP
    Size                : 8
================================ Windows
$I30:$INDEX_ROOT
    Size                : 432
$I30:$INDEX_ALLOCATION
    Size                : 16,384
$I30:$BITMAP
    Size                : 8
================================ Windows.old
$I30:$INDEX_ROOT
    Size                : 56
$I30:$INDEX_ALLOCATION
    Size                : 4,096
$I30:$BITMAP
    Size                : 8

ls -l 显示的大小不是目录内文件的总大小时,*nix 上也会发生同样的事情:

在C++17中有std::filesystem::directory_entry获取目录信息