如何复制某些文件(没有文件夹层次结构),但不要覆盖现有文件?

时间:2022-03-20 07:08:58

I need to copy all *.doc files (but not folders whose names match *.doc) from a network folder \\server\source (including files in all nested folders) to a local folder C:\destination without preserving the nested folders hierarchy (i.e. all files should go directly into C:\destination and no nested folders should be created in C:\destination). In case there are several files with the same name from different subfolders of \\server\source, only the first one should be copied and never overwritten then — all conflicting files found later should be skipped (there could be many cases like this, and the skipped files should not be trasferred over the network, otherwise it would take too much time). Here is my attempt to implement it in PowerShell:

我需要将所有* .doc文件(但不是名称与* .doc匹配的文件夹)从网络文件夹\\ server \ source(包括所有嵌套文件夹中的文件)复制到本地文件夹C:\ destination,而不保留嵌套文件夹层次结构(即所有文件应直接进入C:\ destination,并且不应在C:\ destination中创建嵌套文件夹)。如果来自\\ server \ source的不同子文件夹中有多个具有相同名称的文件,则只应复制第一个文件并且永远不会覆盖 - 之后发现的所有冲突文件都应该被跳过(可能有很多这样的情况,并且跳过的文件不应该通过网络传输,否则需要花费太多时间)。这是我尝试在PowerShell中实现它:

cp \\server\source\* -Recurse -Include *.doc -Container:$false -Destination C:\destination

There are at least two problems with this command:

此命令至少存在两个问题:

  • It copies folders whose names match *.doc too.
  • 它复制名称与* .doc匹配的文件夹。
  • In case of conflicting names any file found later is transferred over the network and overwrites the previous one.
  • 如果名称冲突,以后找到的任何文件都将通过网络传输并覆盖前一个文件。

Can you suggest how to fix these problems?
Implementations using copy, xcopy, robocopy, cscript or *.bat, *.cmd are also welcome.
The local OS is Windows 8 and the file system is NTFS.

你能建议如何解决这些问题吗?使用copy,xcopy,robocopy,cscript或* .bat,* .cmd的实现也是受欢迎的。本地操作系统是Windows 8,文件系统是NTFS。

5 个解决方案

#1


14  

I would produce the list of files first and validate as you go through the list.

我会先生成文件列表,然后在列表中进行验证。

Something like this:

像这样的东西:

$srcdir = "\\server\source\";
$destdir = "C:\destination\";
$files = (Get-ChildItem $SrcDir -recurse -filter *.doc | where-object {-not ($_.PSIsContainer)});
$files|foreach($_){
    if (!([system.io.file]::Exists($destdir+$_.name))){
                cp $_.Fullname ($destdir+$_.name)
    };
}

So, use Get-ChildItem to list files in source folder matching the filter, pipe through where-object to strip directories out.

因此,使用Get-ChildItem列出匹配过滤器的源文件夹中的文件,通过where-object管道去除目录。

Then go through each file in a foreach loop and check if the filename (not Fullname) exists in the destination using the Exists method of the system.io.file .NET class.

然后遍历foreach循环中的每个文件,并使用system.io.file .NET类的Exists方法检查目标中是否存在文件名(不是Fullname)。

If it doesn't, copy, using only original filename (dropping original path).

如果没有,请仅使用原始文件名(删除原始路径)进行复制。

Use the -whatif option on the copy when testing, so it only displays what it would do, in case result is not what you wanted :-)

测试时在副本上使用-whatif选项,因此它只显示它会做什么,以防结果不是你想要的:-)

#2


6  

The previous answers seem rather overcomplicated to me, unless I'm misunderstanding something. This should work:

以前的答案对我来说似乎过于复杂,除非我误解了一些东西。这应该工作:

Get-ChildItem "\\server\source\" *.doc -Recurse | ?{-not ($_.PSIsContainer -or (Test-Path "C:\Destination\$_"))} | Copy-Item -Destination "C:\Destination"

None of the built-in commands - copy, xcopy, or robocopy - will do what you want on their own, but there's a utility called xxcopy that will, conveniently available at http://www.xxcopy.com. It has a number of built-in options specifically for flattening directory trees into a single directory. The following will do what you described:

内置命令(复制,xcopy或robocopy)都不会自行执行您想要的操作,但是有一个名为xxcopy的实用程序可以在http://www.xxcopy.com上方便地找到。它有许多内置选项,专门用于将目录树展平为单个目录。以下将按您的描述进行:

xxcopy "\\server\source\*.doc" "C:\Destination" /SGFO

However, xxcopy has various other options for handling duplicate filenames than just copying the first one encountered, such as adding the source directory name to the filename, or adding sequential numerical identifies to all but the first one, or all but the newest or oldest. See this page for details: http://www.xxcopy.com/xxcopy16.htm

但是,xxcopy具有处理重复文件名的各种其他选项,而不仅仅是复制遇到的第一个文件名,例如将源目录名添加到文件名,或者向除第一个之外的所有文件添加顺序数字标识,或者除最新或最旧之外的所有文件添加顺序数字标识。有关详细信息,请参阅此页面:http://www.xxcopy.com/xxcopy16.htm

#3


2  

# Get all *.doc files under \\server\source
Get-ChildItem -Path \\server\source *.doc -Recurse |
    # Filter out directores
    Where-Object { -not $_.PsIsContainer } | 
    # Add property for destination
    Add-Member ScriptProperty -Name Destination -Value { Join-Path 'C:\destination' $this.Name } -PassThru |
    # Filter out files that exist on the destination
    Where-Object { -not (Test-Path -Path $_.Destination -PathType Leaf } | 
    # Copy. 
    Copy-Item

#4


1  

Why use foreach when you already have a pipeline? Calculated properties for the win!

为什么在已有管道的情况下使用foreach?获胜的计算属性!

Get-ChildItem -Recurse -Path:\\Server\Path -filter:'*.doc' | 
    Where { -not $_.PSIsContainer } |
    Group Name |
    Select @{Name='Path'; Expression={$_.Group[0].FullName}},@{Name='Destination'; Expression={'C:\Destination\{0}' -f $_.Name}} |
    Copy-Item

#5


0  

$docFiles = Get-ChildItem -Path "\\server\source" -Recurse | Where-Object {$_.Attributes.ToString() -notlike "*Directory*" -and ($_.Name -like "*.doc" -or $_.Name -like "*.doc?")} | Sort-Object -Unique;
$docFiles | ForEach-Object { Copy-Item -Path $_.fullname -Destination "C:\destination" };

First line read each *.doc file and *.doc? (so it considers also Office 2010 .docx format), excluding Directories and duplicate files.
Second line copy each item from destination to source (the folder C:\destination must already exist).
In general I suggest you to split command over multiple lines because it's easier to produce code (in this case first task: get files, second task: copy files).

第一行读取每个* .doc文件和* .doc? (因此它也考虑Office 2010 .docx格式),不包括目录和重复文件。第二行将每个项目从目标复制到源(文件夹C:\ destination必须已存在)。一般来说,我建议你将命令分成多行,因为它更容易生成代码(在这种情况下,第一个任务:获取文件,第二个任务:复制文件)。

#1


14  

I would produce the list of files first and validate as you go through the list.

我会先生成文件列表,然后在列表中进行验证。

Something like this:

像这样的东西:

$srcdir = "\\server\source\";
$destdir = "C:\destination\";
$files = (Get-ChildItem $SrcDir -recurse -filter *.doc | where-object {-not ($_.PSIsContainer)});
$files|foreach($_){
    if (!([system.io.file]::Exists($destdir+$_.name))){
                cp $_.Fullname ($destdir+$_.name)
    };
}

So, use Get-ChildItem to list files in source folder matching the filter, pipe through where-object to strip directories out.

因此,使用Get-ChildItem列出匹配过滤器的源文件夹中的文件,通过where-object管道去除目录。

Then go through each file in a foreach loop and check if the filename (not Fullname) exists in the destination using the Exists method of the system.io.file .NET class.

然后遍历foreach循环中的每个文件,并使用system.io.file .NET类的Exists方法检查目标中是否存在文件名(不是Fullname)。

If it doesn't, copy, using only original filename (dropping original path).

如果没有,请仅使用原始文件名(删除原始路径)进行复制。

Use the -whatif option on the copy when testing, so it only displays what it would do, in case result is not what you wanted :-)

测试时在副本上使用-whatif选项,因此它只显示它会做什么,以防结果不是你想要的:-)

#2


6  

The previous answers seem rather overcomplicated to me, unless I'm misunderstanding something. This should work:

以前的答案对我来说似乎过于复杂,除非我误解了一些东西。这应该工作:

Get-ChildItem "\\server\source\" *.doc -Recurse | ?{-not ($_.PSIsContainer -or (Test-Path "C:\Destination\$_"))} | Copy-Item -Destination "C:\Destination"

None of the built-in commands - copy, xcopy, or robocopy - will do what you want on their own, but there's a utility called xxcopy that will, conveniently available at http://www.xxcopy.com. It has a number of built-in options specifically for flattening directory trees into a single directory. The following will do what you described:

内置命令(复制,xcopy或robocopy)都不会自行执行您想要的操作,但是有一个名为xxcopy的实用程序可以在http://www.xxcopy.com上方便地找到。它有许多内置选项,专门用于将目录树展平为单个目录。以下将按您的描述进行:

xxcopy "\\server\source\*.doc" "C:\Destination" /SGFO

However, xxcopy has various other options for handling duplicate filenames than just copying the first one encountered, such as adding the source directory name to the filename, or adding sequential numerical identifies to all but the first one, or all but the newest or oldest. See this page for details: http://www.xxcopy.com/xxcopy16.htm

但是,xxcopy具有处理重复文件名的各种其他选项,而不仅仅是复制遇到的第一个文件名,例如将源目录名添加到文件名,或者向除第一个之外的所有文件添加顺序数字标识,或者除最新或最旧之外的所有文件添加顺序数字标识。有关详细信息,请参阅此页面:http://www.xxcopy.com/xxcopy16.htm

#3


2  

# Get all *.doc files under \\server\source
Get-ChildItem -Path \\server\source *.doc -Recurse |
    # Filter out directores
    Where-Object { -not $_.PsIsContainer } | 
    # Add property for destination
    Add-Member ScriptProperty -Name Destination -Value { Join-Path 'C:\destination' $this.Name } -PassThru |
    # Filter out files that exist on the destination
    Where-Object { -not (Test-Path -Path $_.Destination -PathType Leaf } | 
    # Copy. 
    Copy-Item

#4


1  

Why use foreach when you already have a pipeline? Calculated properties for the win!

为什么在已有管道的情况下使用foreach?获胜的计算属性!

Get-ChildItem -Recurse -Path:\\Server\Path -filter:'*.doc' | 
    Where { -not $_.PSIsContainer } |
    Group Name |
    Select @{Name='Path'; Expression={$_.Group[0].FullName}},@{Name='Destination'; Expression={'C:\Destination\{0}' -f $_.Name}} |
    Copy-Item

#5


0  

$docFiles = Get-ChildItem -Path "\\server\source" -Recurse | Where-Object {$_.Attributes.ToString() -notlike "*Directory*" -and ($_.Name -like "*.doc" -or $_.Name -like "*.doc?")} | Sort-Object -Unique;
$docFiles | ForEach-Object { Copy-Item -Path $_.fullname -Destination "C:\destination" };

First line read each *.doc file and *.doc? (so it considers also Office 2010 .docx format), excluding Directories and duplicate files.
Second line copy each item from destination to source (the folder C:\destination must already exist).
In general I suggest you to split command over multiple lines because it's easier to produce code (in this case first task: get files, second task: copy files).

第一行读取每个* .doc文件和* .doc? (因此它也考虑Office 2010 .docx格式),不包括目录和重复文件。第二行将每个项目从目标复制到源(文件夹C:\ destination必须已存在)。一般来说,我建议你将命令分成多行,因为它更容易生成代码(在这种情况下,第一个任务:获取文件,第二个任务:复制文件)。