【问题标题】:Recursive for loop in batch files without following symlinks批处理文件中的递归for循环而不遵循符号链接
【发布时间】:2019-10-28 10:06:11
【问题描述】:

除了不遵循符号链接之外,在批处理文件中是否有一种简单的方法来循环包括子目录在内的文件(如下所示)?

FOR /R %%F IN (*) DO (...)

我在for帮助中没有看到这样的标志...

上下文是我正在编写一个循环文件​​并创建符号链接的批处理文件。它需要通过子目录,但是当有针对目录的符号链接时,它不应该通过它们。对于我当前的FOR /R 循环实现,问题在于它将此类符号链接视为子目录并在其目标内容中运行脚本。我不在乎解决方案是否排除指向普通文件的符号链接(而不是 dirs)。

感谢您的帮助

编辑澄清:

例如,提供这个目录结构:

dir
 |-- A.txt
 |-- subDir
 |     |-- B.txt
 |-- linkDir = symlink to targetDir
 |-- Z.txt = symlink to targetDir/C.txt

targetDir
 |-- C.txt

当我在dir 上运行FOR /R 循环时,它会按预期处理A.txtB.txt,但也会处理我不想要的C.txt。这是因为FOR /R 指令在构建要迭代的文件列表时遵循符号链接,因此它就像linkDir 是一个包含C.txt 的常规子目录。

关于Z.txt,循环是否读取它并不重要。 FOR /R 实现实际上会处理它,但我设置了一个过滤器以在 %%F 是符号链接时跳过迭代。

问题是我不知道如何告诉FOR 不要将C.txt 包含在列表中,也不知道当%%FtargetDir\C.txt 时如何知道该文件在列表中,因为它遵循符号链接在路径dir\subDir\C.txt

【问题讨论】:

  • 在文件/目录上尝试ATTRIB 命令。检查/L 开关...
  • FOR /F "EOL=? DELIMS=" %%F IN ('DIR /B /S /A:-L') DO … 您可能还希望查看DIR 命令的其他选项,即打开命令提示符窗口并输入dir /?
  • @Aacini ATTRIB不只是为了改变属性吗?
  • @Compo 这是一个很好的尝试,它不包括指向普通文件的符号链接,但不幸的是 /S 标志仍然跟随符号链接......
  • @fbastien,鉴于您只提供了非常有限的关于您要实现的目标的想法,我不可能更具体。正如我之前的评论中提到的,当您也使用其他选项时会发生什么?例如FOR /F "EOL=? DELIMS=" %%F IN ('DIR /B /S /A:-D-S-L') DO …

标签: windows batch-file


【解决方案1】:

您可以编写自己的递归函数,检查每个文件/目录的链接。
这有点慢,因为每个文件/目录都有%%~aX 访问权限。

@echo off

call :recurseDir .
exit /b

:recurseDir
rem echo ----- :recurseDir "%~1" -------
setlocal EnableDelayedExpansion
pushd "%~1"
FOR /F "delims=" %%X in ('dir /b') DO (
    set "attr=%%~aX"

    if "!attr:~8,1!" NEQ "l" (
        if "!attr:~0,1!" EQU "d" (
            call :recurseDir "%%~fX"
        ) ELSE (
            call echo :processFile "%%~fX"
        )
    )
)
popd

为了加快速度,您可以拆分文件和目录的逻辑。
然后只需要检查带有%%~aX 的目录中的符号链接。
此版本不再需要延迟扩展,因为它通过将字符 l 处的属性拆分为 d-------l-- 来检测符号链接。
因为 Win 7 只显示d-------l,所以必须在属性上附加至少一个虚拟字符(我选择#

@echo off

call :recurseDir .
exit /b

:recurseDir
echo ----- :recurseDir "%~1" -------
pushd "%~1"
FOR /F "delims=" %%X in ('dir /b /a:-l-d 2^> nul') DO (
    call echo :processFile "%%~fX"
)
FOR /D %%X in (*) DO (  
    set "symlink="
    for /F "tokens=2 delims=l" %%A in ("%%~aX #") DO set "symlink=1"
    if not defined symlink (
        call :recurseDir "%%~fX"
    )
)
popd

【讨论】:

  • 非常感谢,它有效!我不得不调整第二个版本,因为 symlink 变量从未设置为 1 并且pushd 在给定符号链接时抛出错误。我的解决方法是在嵌套循环中的 %%~aX 之后添加一个随机字符。我猜那是因为l 字符是属性列表中的最后一个字符,所以即使它存在,第二个标记也是空的,for 命令会忽略它。在我接受你的回答之前,我正在等待看看是否有人提出了没有子程序的解决方案。
  • 我很惊讶l 是您属性列表中的最后一个字符!我的列表看起来像 d-------l-- 的符号链接(Win 10.0.17134.1006),您使用的是哪个版本?但是您在"%%~aX 添加字符的解决方案应该可以工作。我刚刚改变了我对那个案例的回答
  • 我想知道为什么 pushd <symlink> 在你的情况下失败,因为这对我来说没有问题。但这对您的问题仍然不重要,因为您根本不想遵循符号链接。您确定您的符号链接指向现有目录吗?
  • 我使用的是 Win7 SP1。是的,pushd <symlink> 失败是我的错:我使用的是相对路径并使用斜杠而不是反斜杠... ^^
  • @fbastien 我用Win7检查过,发现你描述的行为,属性较少。 attrib /? 的选项也更少
【解决方案2】:

试试:

@echo off
setlocal EnableDelayedExpansion

for /R /D %%f in (*) do (
   set "attr=%%~Af"
   if "!attr:~8,1!" neq "l" (
      echo Files in this not-symlink dir:
      dir /B /A:-D "%%f"
   )
)

【讨论】:

  • 在许多情况下是一个很好的解决方案,但有一个缺点,即完全搜索符号链接目录。当符号链接指向 parent 目录 时,这甚至会导致死锁
  • 这不是我的问题的有效解决方案,因为它输出targetDir(即C.txt)的内容。这里的难点不是排除针对普通文件的符号链接,而是排除通过符号链接目录访问的子目录中的文件。
  • 我发现了这个解决方案的问题。它检测符号链接目录,但符号链接目录的子目录被视为有效目录。所以它只有在符号链接目录没有任何子目录时才有效
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-04-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-11-06
  • 2012-09-19
相关资源
最近更新 更多