如何复制目录结构但仅包含特定文件(使用Windows批处理文件)

127

正如标题所述,我该如何递归地复制一个目录结构,但只包含其中的一些文件呢?例如,给定以下目录结构:

folder1
  folder2
    folder3
      data.zip
      info.txt
      abc.xyz
    folder4
    folder5
      data.zip
      somefile.exe
      someotherfile.dll

文件data.zipinfo.txt可能出现在目录结构的任何位置。 我该如何复制完整的目录结构,但只包括名为data.zip和info.txt的文件(所有其他文件都应被忽略)?

生成的目录结构应该是这样的:

copy_of_folder1
  folder2
    folder3
      data.zip
      info.txt
    folder4
    folder5
      data.zip
15个回答

199

您没有提到是否只能使用批处理,但如果您可以使用ROBOCOPY,请尝试以下方法:

ROBOCOPY C:\Source C:\Destination data.zip info.txt /E

编辑:将/S参数更改为/E以包括空文件夹。


1
我能用这个语法复制*.zip吗?我尝试过只写*.zip而不是data.zip,但它没有复制文件,只有文件夹。我还尝试了?.zip。 - Niels Brinch
1
@Niels Brinch 是的,你应该能够做到。你使用的确切命令行是什么? - aphoria
1
ROBOCOPY C:\Source C:\Destination *.zip /E请执行以上命令。 - Niels Brinch
1
真的吗?我也在使用Win 7。当我尝试复制文件时,它只会复制文件夹。我会再试一次。(顺便问一下,如何在语法中包含多个*.zip - 比如*.zip和*.jpg?) - Niels Brinch
1
@AntonDuzenko 也许 ROBOCOPY 的 MOVMOVE 参数可以帮到你。在命令行中运行 ROBOCOPY /? 以查看更多信息。在使用任何重要文件之前,请先设置一些测试文件夹和文件。 - aphoria
显示剩余4条评论

29

另一种解决方案是逐个复制文件,不需要使用ROBOCOPY:

@echo off
setlocal enabledelayedexpansion

set "SOURCE_DIR=C:\Source"
set "DEST_DIR=C:\Destination"
set FILENAMES_TO_COPY=data.zip info.txt

for /R "%SOURCE_DIR%" %%F IN (%FILENAMES_TO_COPY%) do (
    if exist "%%F" (
        set FILE_DIR=%%~dpF
        set FILE_INTERMEDIATE_DIR=!FILE_DIR:%SOURCE_DIR%=!
        xcopy /E /I /Y "%%F" "%DEST_DIR%!FILE_INTERMEDIATE_DIR!"
    )
)

外层 for 语句生成 SOURCE_DIR 中的子目录和 FILENAMES_TO_COPY 中的文件名的所有可能路径组合。对于每个已存在的文件,都会调用 xcopy。FILE_INTERMEDIATE_DIR 包含文件在 SOURCE_DIR 中的子目录路径,需要在 DEST_DIR 中创建。


1
我需要从文件夹和子文件夹中复制所有照片,这个批处理脚本很有帮助。只需将上面的一行“set FILENAMES_TO_COPY=data.zip info.txt”更改为“set FILENAMES_TO_COPY=*.jpg”。 - Avatar
我遇到了一个错误,因为文件路径上有空格 (SOURCE_DIR="C:\Origin Test", DEST_DIR="C:\Dest Test", error: Test"" was unexpected at this moment)。我已经尝试在所有地方将%var%更改为!var!,但是没有成功。如何解决这个问题?整个目录结构都充满了带有空格的文件夹名称。 - ricardo.scholz
1
尝试使用以下命令:set "SOURCE_DIR=C:\Origin Test"set "DEST_DIR=C:\Dest Test"。请注意双引号的位置。 - sakra

16
要将当前目录及其所有子目录中的所有文本文件复制到G驱动器,并保留目录结构,请执行以下操作:
xcopy .\*.txt /s G:

在这个命令中,.\ 部分表示当前目录,而 /s 则确保所有子目录也被遍历到。

这个能够递归地工作吗,比如从子文件夹中复制所有的 .txt 文件并保留相对于基础目录的目录结构? - Micka
2
@Micka 是的,它可以。 - Anurag S Sharma

16
尝试将find的输出(即文件路径)导入cpio。
find . -type f -name '*.jpg' | cpio -p -d -v targetdir/

cpio会检查目标文件的时间戳,因此它是安全且快速的。

一旦你习惯了它,可以删除-v以获得更快的操作。


5
该软件有一个依赖项需要安装 cpio,而 cpio 不是标准安装的一部分。 - Crashworks
我对这个在Windows上应该如何起作用感到困惑。 - Mike 'Pomax' Kamermans

12
如果Powershell是一个选项,你可以这样做:

如果 PowerShell 是可选的,您可以执行以下操作:

Copy-Item c:\sourcePath d:\destinationPath -filter data.zip -recurse

主要的缺点是它会复制所有文件夹,即使它们最终会因为没有匹配您指定的过滤器而变为空。因此,您可能会得到一个充满空文件夹的树形结构,除了少数具有所需文件的文件夹。


再次运行此命令将更改目标位置(它会将文件放在子文件夹中),如果您问我,这是一个愚蠢的行为。在复制之前,您需要运行“rm -r -ErrorAction:SilentlyContinue d:\destinationPath”来解决这个问题。 - MakotoE

7
感谢之前的回答。 :)
这个名为"r4k4copy.cmd"的脚本:
@echo off
for %%p in (SOURCE_DIR DEST_DIR FILENAMES_TO_COPY) do set %%p=
cls
echo :: Copy Files Including Folder Tree
echo :: http://stackoverflow.com
rem     /questions/472692/how-to-copy
rem     -a-directory-structure-but-only
rem     -include-certain-files-using-windows
echo :: ReScripted by r4k4
echo.
if "%1"=="" goto :NoParam
if "%2"=="" goto :NoParam
if "%3"=="" goto :NoParam
setlocal enabledelayedexpansion
set SOURCE_DIR=%1
set DEST_DIR=%2
set FILENAMES_TO_COPY=%3
for /R "%SOURCE_DIR%" %%F IN (%FILENAMES_TO_COPY%) do (
if exist "%%F" (
set FILE_DIR=%%~dpF
set FILE_INTERMEDIATE_DIR=!FILE_DIR:%SOURCE_DIR%=!
xcopy /E /I /Y "%%F" "%DEST_DIR%!FILE_INTERMEDIATE_DIR!"
)
)
goto :eof
:NoParam
echo.
echo Syntax: %0 [Source_DIR] [Dest_DIR] [FileName]
echo Eg.   : %0 D:\Root E:\Root\Lev1\Lev2\Lev3 *.JPG
echo Means : Copy *.JPG from D:\Root to E:\Root\Lev1\Lev2\Lev3

它接受“源”,“目标”和“文件名”的变量。 它还可以仅复制指定类型的文件或选择性文件名。 欢迎任何改进。 :)

6

只使用find和cp命令:

mkdir /tmp/targetdir
cd sourcedir
find . -type f -name '*.zip' -exec cp -p --parents {} /tmp/targetdir ";"
find . -type f -name '*.txt' -exec cp -p --parents {} /tmp/targetdir ";"

4
类似于Paulius的解决方案,但是您不关心的文件不会被复制然后删除:
@echo OFF

:: Replace c:\temp with the directory where folder1 resides.
cd c:\temp

:: You can make this more generic by passing in args for the source and destination folders.
for /f "usebackq" %%I in (`dir /b /s /a:-d folder1`) do @echo %%~nxI | find /V "data.zip" | find /v "info.txt" >> exclude_list.txt
xcopy folder1 copy_of_folder1 /EXCLUDE:exclude_list.txt /E /I

3

只需要两个简单的命令,但我不建议使用此方法,除非你不需要复制的文件很小。这是因为它会复制所有文件,然后再删除不需要复制的文件。

xcopy /E /I folder1 copy_of_folder1
for /F "tokens=1 delims=" %i in ('dir /B /S /A:-D copy_of_files ^| find /V "info.txt" ^| find /V "data.zip"') do del /Q "%i"

当然,第二个命令有点长,但是它有效!

此外,这种方法不需要你下载和安装任何第三方工具(Windows 2000+ BATCH已经有足够的命令来实现此功能)。


非常感谢。正如您所提到的,这并不理想,因为(在我的情况下)必须复制的文件通常很小,而不应复制的文件较大(可能有很多)。 - M4N

2
对于那些使用Altap Salamander(双面板文件管理器)的人:在复制弹出窗口的选项中,只需指定文件名或掩码即可。很容易。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接