批处理文件:列出目录中的所有文件以及相对路径

发布于 2024-12-19 17:02:38 字数 895 浏览 0 评论 0原文

关于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

是否可能?

如果我的帖子太令人困惑:我基本上想要 在 Linux CLI 中以相对于当前目录的路径递归列出文件,但仅适用于 Windows。

Concerning Windows batch files: Is there a way to list all the files (or all of a specific type) in a certain directory and its subdirectories, including the paths relative to the current (or the search) directory in the list?

For example, if I want all the .txt files in the current directory and subdirectories with their full paths, I can do

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

or

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

and I will get something like

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

If I do

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

I will get something like

Doc1.txt
Doc2.txt
Doc3.txt

But what I really want is:

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

Is it possible?

If my post is too confusing: I basically want List files recursively in Linux CLI with path relative to the current directory, but just for Windows.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

云之铃。 2024-12-26 17:02:38

迭代目录树并列出相对文件路径的最简单(但不是最快)的方法是使用 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

The simplest (but not the fastest) way to iterate a directory tree and list relative file paths is to use FORFILES.

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

The relative paths will be quoted with a leading .\ as in

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

To remove quotes:

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

To remove quotes and the leading .\:

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
)

or without using delayed expansion

for /f "tokens=1* delims=\" %%A in (
  'forfiles /s /m *.txt /c "cmd /c echo @relpath"'
) do for %%F in (^"%%B) do echo %%~F
别念他 2024-12-26 17:02:38

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

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
)

You could simply get the character length of the current directory, and remove them from your absolute list

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
)
风吹短裙飘 2024-12-26 17:02:38

对于包含等号 (=) 的根路径,此答案无法正常工作。 (感谢 @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。)< /em>

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

This answer will not work correctly with root paths containing equal signs (=). (Thanks @dbenham for pointing that out.)


EDITED: Fixed the issue with paths containing !, again spotted by @dbenham (thanks!).

Alternatively to calculating the length and extracting substrings you could use a different approach:

  • store the root path;

  • clear the root path from the file paths.

Here's my attempt (which worked for me):

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

The r variable is assigned with the current directory. Unless the current directory is the root directory of a disk drive, it will not end with \, which we amend by appending the character. (No longer the case, as the script now reads the __CD__ variable, whose value always ends with \ (thanks @jeb!), instead of CD.)

In the loop, we store the current file path into a variable. Then we output the variable, stripping the root path along the way.

故人爱我别走 2024-12-26 17:02:38

当然,您可以在 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

Of course, you may write a recursive algorithm in Batch that gives you exact control of what you do in every nested subdirectory:

@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
晒暮凉 2024-12-26 17:02:38
@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
)
@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
)
十年不长 2024-12-26 17:02:38

您可以(误)使用 xcopy 命令1 可以列出要复制的文件的相对路径,并具有选项 /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 个文件

C:Doc1.txt
C:子目录\Doc2.txt
C:子目录\Doc3.txt
3 个文件

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

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


要删除摘要行...文件,让 < code>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 ":"

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

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
子目录\Doc2.txt
子目录\Doc3.txt


1)基本方法源自此答案由用户MC ND

You could (mis-)use the xcopy command1 that can list relative paths of files to be copied and that features the option /L to not copy but just list files that would be copied without.

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%"

This would produce an output like such:

.\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)

The destination can be an arbitrary directory path on an existing and accessible drive that does not reside within the source directory tree.

Note that this only works with file but not with directory paths.


To delete the summary line … File(s) let findstr filter it out:

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 "^.:"

Alternatively use find to filter such lines out:

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 ":"

To remove the .\ or the drive prefix, capture the xcopy output with for /F and use an appropriate delimiter:

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
)

It should be quite obvious how to do the same for the drive prefix:

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
)

This is the related sample output:

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

1) The basic approach derives from this answer by user MC ND.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文