# Loop Files

检索指定的文件或文件夹, 每次一个.

    Loop Files FilePattern , Mode

## 参数

FilePattern

  类型: 字符串

  单个文件或文件夹的名称, 或者通配符模式, 如 "C:\Temp\*.tmp". 如果未指定绝对路径, 则假定 FilePattern 在 A_WorkingDir 中.

  支持星号和问号作为通配符使用. 当模式出现在文件的长/普通名称或其 8.3 短名称时出现匹配.

  如果此参数为单个文件或文件夹(即不含通配符) 且 Mode 包含 R, 同时指定的文件名出现在多个被搜索的文件夹中, 那么将找到多个匹配.

  由于系统限制(MAX_PATH), 超过 259 个字符的模式可能无法找到任何文件. 这个限制可以通过使用 `\\?\` 长路径前缀绕过, 但有一些条件.

Mode

  类型: 字符串

  如果为空或省略, 则只包括文件且不递归子文件夹. 否则, 指定一个或多个下列字母:

  - D = 包含目录(文件夹).
  - F = 包含文件. 如果同时省略 F 和 D, 则仅包含文件而不包括目录.
  - R = 递归到子目录(子文件夹). 所有子文件夹都将被递归到其中, 而不仅仅是那些名称匹配 FilePattern 的子文件夹. 如果省略 R, 则不包含子目录中的文件和目录.

## 可在文件循环中使用的特殊变量

下列变量存在于任何文件循环中. 如果一个内层文件循环包含在一个外层文件循环中, 那么最内层循环的文件将具有优先权:

变量 | 描述
--- | ---
A_LoopFileName | 当前检索的文件或文件夹的名称(不包括路径).
A_LoopFileExt | 文件的扩展名(例如 TXT, DOC 或 EXE). 不包括点号(.).
A_LoopFilePath | 当前检索的文件/文件夹的路径和名称. 如果 FilePattern 包含相对路径而不是绝对路径, 这里的路径也将是相对路径. 此外, FilePattern 中的任何短(8.3) 文件夹名称仍将为短名称(参阅下一项目以获得长文件名).
A_LoopFileFullPath | 这与 A_LoopFilePath 有以下不同: 1) 它总是包含文件的绝对/完整路径, 即使 FilePattern 包含相对路径; 2) FilePattern 本身的任何短(8.3) 文件夹名都会被转换为长文件名; 3) FilePattern 中的字符会被转换为大写或小写以匹配文件系统中存储的大小写. 这对于将文件名 -- 例如作为命令行参数传入脚本的文件名 -- 转换为资源管理器显示的准确路径名很有用.
A_LoopFileShortPath | 当前检索文件/文件夹的 8.3 短路径和名称. 例如: C:\MYDOCU~1\ADDRES~1.txt. 如果 FilePattern 包含相对路径而不是绝对路径, 这里的路径也将是相对路径. 要检索单个文件或文件夹的完整的 8.3 路径和名称, 如下例所示:
    Loop Files, "C:\My Documents\Address List.txt"
        ShortPathName := A_LoopFileShortPath
注意: 如果文件没有短文件名, 这个变量将为空, 这可能发生在注册表中设置了 NtfsDisable8dot3NameCreation 的系统中. 如果 FilePattern 包含一个相对路径并且循环的主体使用 SetWorkingDir 从循环本身的工作目录中切换出来, 那么这个变量也将是空白的.
A_LoopFileShortName | 文件的 8.3 短名称或备用名称. 如果文件没有短文件名(由于长文件比 8.3 短, 或者可能因为 NTFS 文件系统禁用了短文件名的生成), 将检索 A_LoopFileName.
A_LoopFileDir | A_LoopFileName 所在目录的路径. 如果 FilePattern 包含相对路径而不是绝对路径, 那么这里的路径也将是相对路径. 根目录不会包含反斜杠. 例如: C:
A_LoopFileTimeModified | 文件最后修改的时间. 格式为 YYYYMMDDHH24MISS.
A_LoopFileTimeCreated | 文件创建的时间. 格式为 YYYYMMDDHH24MISS.
A_LoopFileTimeAccessed | 文件最后访问的时间. 格式为 YYYYMMDDHH24MISS.
A_LoopFileAttrib | 当前检索文件的属性.
A_LoopFileSize | 当前检索文件的大小, 以字节为单位. 支持大于 4 GB 的文件.
A_LoopFileSizeKB | 当前检索文件的大小, 以 KB 为单位, 向下取整到最近的整数.
A_LoopFileSizeMB | 当前检索文件的大小, 以 Mb 为单位, 向下取整到最近的整数.

## 备注

当你想对一系列文件和/或文件夹进行操作时, 文件循环非常有用, 每次一个.

所有匹配的文件都会被检索出来, 包括隐藏的文件. 相比之下, 操作系统的功能, 如 DIR 命令, 默认情况下会省略隐藏文件. 为了避免处理隐藏的, 系统的, 和/或只读的文件, 在循环中使用类似下面的方法:

    if A_LoopFileAttrib ~= "[HRS]"  ; 跳过任何具有 H(隐藏), R(只读) 或 S(系统). 请参阅 ~= 运算符.
        continue  ; 跳过这个文件并继续下一个文件.

在递归搜索过程中, 要检索文件的相对路径而不是绝对路径, 在循环之前使用 SetWorkingDir 改变基文件夹, 然后在循环中省略路径(例如 `Loop Files, "*.*", "R"`). 这将导致 A_LoopFilePath 包含相对于基文件夹的文件路径.

如果一个文件循环在它自己的权限范围内创建或重命名文件或文件夹, 它可能会自乱阵脚. 例如, 如果通过 FileMove 或其他方式重命名文件, 每个这样的文件可能会被发现两次: 一次是旧名, 另一次是新名. 为了解决这个问题, 只有在创建文件列表后才重命名这些文件. 例如:

    FileList := ""
    Loop Files, "*.jpg"
        FileList .= A_LoopFileName "`n"
    Loop Parse, FileList, "`n"
        FileMove A_LoopField, "renamed_" A_LoopField

在 NTFS 文件系统中的文件可能总是按字母顺序来获取. 在其他文件系统中的文件则没有特殊的获取顺序. 要使用特殊的获取顺序, 使用 Sort 命令, 如同下文示例部分演示的那样.

支持超过 259 个字符的文件模式, 仅在满足至少一个以下情况时有效:

- 系统启用了长路径支持(需要 Windows 10 版本 1607 或更高).
- 使用了 `\\?\` 长路径前缀(适用注意事项).

在所有其他情况下, 超过 259 个字符的文件模式将不会找到任何文件或文件夹. 这个限制既适用于 FilePattern, 也适用于递归到子文件夹时使用的任何临时模式.

可以选择使用 One True Brace(OTB) 样式, 它允许左大括号出现在同一行而不是下面. 例如: `Loop Files "*.txt", "R" {`.

请参阅 Loop 了解关于区块, Break, Continue 和 A_Index 变量(其存在于各种类型的循环中) 的相关信息.

循环后面可以有一个可选的 Else 语句, 如果没有找到匹配的文件或目录(即循环的迭代次数为零), 则执行 Else 语句.

函数 FileGetAttrib, FileGetSize, FileGetTime, FileGetVersion, FileSetAttrib 和 FileSetTime 可以在文件循环中使用, 而不需要它们的 Filename/FilePattern 参数.

## 相关

Loop, Break, Continue, 区块, SplitPath, FileSetAttrib, FileSetTime

## 示例

报告位于目录及其子目录中的每个文本文件的完整路径.

    Loop Files, A_ProgramFiles "\*.txt", "R"  ; 递归子文件夹.
    {
        Result := MsgBox("Filename = " A_LoopFilePath "`n`nContinue?",, "y/n")
        if Result = "No"
            break
    }

计算文件夹的大小, 包括其所有子文件夹中的文件.

    FolderSizeKB := 0
    WhichFolder := DirSelect()  ; 让用户选取文件夹.
    Loop Files, WhichFolder "\*.*", "R"
        FolderSizeKB += A_LoopFileSizeKB
    MsgBox "Size of " WhichFolder " is " FolderSizeKB " KB."

获取根据名称排序的文件名(要以日期排序请参阅下一个例子).

    FileList := ""  ; 初始化为空.
    Loop Files, "C:\*.*"
        FileList .= A_LoopFileName "`n"
    FileList := Sort(FileList, "R")  ; R 选项逆序排列. 有关其他选项, 请参阅 Sort.
    Loop Parse, FileList, "`n"
    {
        if A_LoopField = ""  ; 忽略列表末尾的空项.
            continue
        Result := MsgBox("File number " A_Index " is " A_LoopField ".  Continue?",, "y/n")
        if Result = "No"
            break
    }

获取根据修改日期排序的文件名.

    FileList := ""
    Loop Files, A_MyDocuments "\Photos\*.*", "FD"  ; 包含文件和子目录
        FileList .= A_LoopFileTimeModified "`t" A_LoopFileName "`n"
    FileList := Sort(FileList)  ; 根据日期排序.
    Loop Parse, FileList, "`n"
    {
        if A_LoopField = "" ; 忽略列表末尾的最后一个换行符(空项).
            continue
        FileItem := StrSplit(A_LoopField, A_Tab)  ; 用 tab 作为分隔符将其分为两部分.
        Result := MsgBox("The next file (modified at " FileItem[1] ") is:`n" FileItem[2] "`n`nContinue?",, "y/n")
        if Result = "No"
            break
    }

仅复制比目标位置中的对应文件新的源文件. 使用源模式(如 "A:\Scripts\*.ahk") 和存在的目标目录(如 "B:\Script Backup") 来调用此函数.

    CopyIfNewer(SourcePattern, Dest)
    {
        Loop Files, SourcePattern
        {
            copy_it := false
            if !FileExist(CopyDest "\" A_LoopFileName)  ; 如果目标文件还不存在, 那么总是复制.
                copy_it := true
            else
            {
                time := FileGetTime(CopyDest "\" A_LoopFileName)
                time := DateDiff(time, A_LoopFileTimeModified, "Seconds")  ; 从目的时间中减去源文件的时间.
                if time < 0  ; 源文件比目的文件新.
                    copy_it := true
            }
            if copy_it
            {
                try
                    FileCopy A_LoopFilePath, CopyDest "\" A_LoopFileName, 1   ; 以覆盖形式复制 overwrite=yes
                catch
                    MsgBox 'Could not copy "' A_LoopFilePath '" to "' CopyDest '\' A_LoopFileName '".'
            }
       }
    }

把通过命令行参数传递的文件名转换为长名称, 完整路径和在文件系统中存储的正确的大写/小写字符形式.

    for GivenPath in A_Args  ; 对于每一个参数(或拖放到脚本中的文件):
    {
        Loop Files, GivenPath, "FD"  ; 包括文件和目录.
            LongPath := A_LoopFilePath
        MsgBox "The case-corrected long path name of file`n" GivenPath "`nis:`n" LongPath
    }

dftg