【问题标题】:Batch files: List all files in a directory with relative paths批处理文件:列出具有相对路径的目录中的所有文件
【发布时间】:2012-01-13 04:50:23
【问题描述】:

关于 Windows 批处理文件:有没有办法列出某个目录及其子目录中的所有文件(或所有特定类型的文件),包括相对于列表中当前(或搜索)目录的路径?

例如,如果我想要当前目录和子目录中的所有 .txt 文件及其完整路径,我可以这样做

for /r . %%g in (*.txt) do echo %%g >> C:\temp\test.txt

dir *.txt /b /s >> C:\temp\test.txt

我会得到类似的东西

C:\test\Doc1.txt
C:\test\subdir\Doc2.txt
C:\test\subdir\Doc3.txt

如果我这样做

for /r . %%g in (*.txt) do echo %%~nxg >> C:\temp\test.txt

我会得到类似的东西

Doc1.txt
Doc2.txt
Doc3.txt

但我真正想要的是:

Doc1.txt
subdir\Doc2.txt
subdir\Doc3.txt

有可能吗?

如果我的帖子太混乱:我基本上想要 List files recursively in Linux CLI with path relative to the current directory,但只适用于 Windows。

【问题讨论】:

    标签: windows batch-file cmd relative-path


    【解决方案1】:

    您可以(错误地)使用xcopy command,它可以列出要复制的文件的相对路径,并且具有选项/L 不复制而只列出不复制的文件。

    rem // First change into the target directory:
    cd /D "C:\test"
    rem // Now let `xcopy` list files relative to the current directory preceded with `.\`:
    xcopy /L /S /Y /R /I ".\*.txt" "%TEMP%"
    rem // Or let `xcopy` list files relative to the current directory preceded with the drive:
    xcopy /L /S /Y /R /I "*.txt" "%TEMP%"
    

    这将产生如下输出:

    .\Doc1.txt
    .\subdir\Doc2.txt
    .\subdir\Doc3.txt
    3 File(s)
    
    C:Doc1.txt
    C:subdir\Doc2.txt
    C:subdir\Doc3.txt
    3 File(s)
    

    目标可以是现有且可访问的驱动器上的任意目录路径,该驱动器不位于源目录树中。

    请注意,这仅适用于文件,但不适用于目录路径。


    要删除摘要行 … File(s)findstr 过滤掉它:

    cd /D "C:\test"
    rem // Filter out lines that do not begin with `.\`:
    xcopy /L /S /Y /R /I ".\*.txt" "%TEMP%" | findstr "^\.\\"
    rem // Filter out lines that do not begin with a drive letter + `:`:
    xcopy /L /S /Y /R /I "*.txt" "%TEMP%" | findstr "^.:"
    

    或者使用find 过滤掉这些行:

    cd /D "C:\test"
    rem // Filter out lines that do not contain `.\`:
    xcopy /L /S /Y /R /I ".\*.txt" "%TEMP%" | find ".\"
    rem // Filter out lines that do not contain `:`:
    xcopy /L /S /Y /R /I "*.txt" "%TEMP%" | find ":"
    

    要删除.\ 或驱动器前缀,请使用for /F 捕获xcopy 输出并使用适当的分隔符:

    cd /D "C:\test"
    rem // The 1st token is a `.`, the remaining token string `*` is going to be empty for the summary line:
    for /F "tokens=1* delims=\" %%I in ('
        xcopy /L /S /Y /R /I ".\*.txt" "%TEMP%"
    ') do (
        rem // Output the currently iterated item but skip the summary line:
        if not "%%J"=="" echo(%%J
    )
    

    如何对驱动器前缀做同样的事情应该很明显:

    cd /D "C:\test"
    rem // The 2nd token is empty for the summary line and is not even going to be iterated:
    for /F "tokens=2 delims=:" %%I in ('
        xcopy /L /S /Y /R /I "*.txt" "%TEMP%"
    ') do (
        rem // Simply output the currently iterated item:
        echo(%%I
    )
    

    这是相关的示例输出:

    Doc1.txt
    subdir\Doc2.txt
    subdir\Doc3.txt
    

    【讨论】:

      【解决方案2】:

      迭代目录树和列出相对文件路径的最简单(但不是最快)方法是使用FORFILES

      forfiles /s /m *.txt /c "cmd /c echo @relpath"
      

      相对路径将以.\ 开头,如

      ".\Doc1.txt"
      ".\subdir\Doc2.txt"
      ".\subdir\Doc3.txt"
      


      要删除引号:

      for /f %%A in ('forfiles /s /m *.txt /c "cmd /c echo @relpath"') do echo %%~A
      


      要删除引号和前导.\

      setlocal disableDelayedExpansion
      for /f "delims=" %%A in ('forfiles /s /m *.txt /c "cmd /c echo @relpath"') do (
        set "file=%%~A"
        setlocal enableDelayedExpansion
        echo !file:~2!
        endlocal
      )
      

      或不使用延迟扩展

      for /f "tokens=1* delims=\" %%A in (
        'forfiles /s /m *.txt /c "cmd /c echo @relpath"'
      ) do for %%F in (^"%%B) do echo %%~F
      

      【讨论】:

      • 当我将值输出到 cmd 时,您的解决方案有效,但如果我回显“@relpath >> cache.appcache”,则仅列出父目录中的文件。你知道是什么原因造成的吗?
      • @rory - 没有线索。如果您确定没有忘记使用 /S 选项,那么这听起来值得提出一个新问题。
      • 绝对不是,它输出子目录到cmd窗口而不是txt文件。我会发布一个新问题,谢谢您的帮助
      • 它正在将子目录文件输出到子目录的新文件中
      • @TimAngus - 它为每次迭代启动一个新的 cmd.exe 进程。是的,与其他形式的迭代相比,它非常慢。但便利是诱人的。
      【解决方案3】:

      您可以简单地获取当前目录的字符长度,并将它们从您的绝对列表中删除

      setlocal EnableDelayedExpansion
      for /L %%n in (1 1 500) do if "!__cd__:~%%n,1!" neq "" set /a "len=%%n+1"
      setlocal DisableDelayedExpansion
      for /r . %%g in (*.log) do (
        set "absPath=%%g"
        setlocal EnableDelayedExpansion
        set "relPath=!absPath:~%len%!"
        echo(!relPath!
        endlocal
      )
      

      【讨论】:

        【解决方案4】:
        @echo on>out.txt
        @echo off
        setlocal enabledelayedexpansion
        set "parentfolder=%CD%"
        for /r . %%g in (*.*) do (
          set "var=%%g"
          set var=!var:%parentfolder%=!
          echo !var! >> out.txt
        )
        

        【讨论】:

          【解决方案5】:

          此答案不适用于包含等号 (=) 的根路径。 (感谢@dbenham 指出这一点。)


          已编辑:修复了包含! 的路径的问题,@dbenham 再次发现(谢谢!)。

          除了计算长度和提取子字符串,您可以使用不同的方法:

          • 存放根路径;

          • 从文件路径中清除根路径。

          这是我的尝试(对我有用):

          @ECHO OFF
          SETLOCAL DisableDelayedExpansion
          SET "r=%__CD__%"
          FOR /R . %%F IN (*) DO (
            SET "p=%%F"
            SETLOCAL EnableDelayedExpansion
            ECHO(!p:%r%=!
            ENDLOCAL
          )
          

          r 变量分配给当前目录。 除非当前目录是磁盘驱动器的根目录,否则它不会以\结尾,我们通过附加字符来修改。 (不再是这种情况,因为现在的脚本读取__CD__ 变量,其值始终以\ 结尾(感谢@jeb!),而不是CD。)

          在循环中,我们将当前文件路径存储到一个变量中。然后我们输出变量,一路剥离根路径。

          【讨论】:

          • 这很好用。任何人都可以在 abc 子文件夹中获取 *.txt 文件:@echo off setlocal DisableDelayedExpansion set "r=%__CD__%abc\" for /r abc %%f in (*.txt) do ( set "p=%%f" setlocal EnableDelayedExpansion echo !p:%r%=! endlocal )
          【解决方案6】:

          当然,您可以在 Batch 中编写递归算法,让您精确控制在每个嵌套子目录中的操作:

          @echo off
          set mypath=
          call :treeProcess
          goto :eof
          
          :treeProcess
          setlocal
          for %%f in (*.txt) do echo %mypath%%%f
          for /D %%d in (*) do (
              set mypath=%mypath%%%d\
              cd %%d
              call :treeProcess
              cd ..
          )
          endlocal
          exit /b
          

          【讨论】:

          • 像魅力一样工作!我喜欢这种方法:-)
          • 也像这样,乍一看似乎没有延迟扩展技巧那么令人费解
          • 最大深度是否有限制(比如 8 个)?
          • @PeterMortensen:是的。嵌套的setlocal 命令的数量存在限制,具体取决于版本。在我的 Windows 8.1 中是 32。
          猜你喜欢
          • 1970-01-01
          • 2010-12-09
          • 1970-01-01
          • 2014-03-05
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-02-04
          相关资源
          最近更新 更多