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

时间:2013-07-05 22:35:00

标签: file powershell copy overwrite flatten

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

\\server\source

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

  • 它复制名称与cp \\server\source\* -Recurse -Include *.doc -Container:$false -Destination C:\destination 匹配的文件夹。
  • 如果名称冲突,以后找到的任何文件都会通过网络传输并覆盖之前的文件。

你能建议如何解决这些问题吗? 我们也欢迎使用*.doccopyxcopyrobocopycscript*.bat的实施方案。
本地操作系统是 Windows 8 ,文件系统是NTFS。

5 个答案:

答案 0 :(得分:15)

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

这样的事情:

$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)
    };
}

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

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

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

在测试时使用副本上的-whatif选项,因此只显示它会执行的操作,以防结果不符合您的要求:-)

答案 1 :(得分:6)

以前的答案对我来说似乎过于复杂,除非我误解了一些事情。这应该有效:

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

没有任何内置命令 - copy,xcopy或robocopy - 可以自己做你想做的事情,但有一个名为xxcopy的实用程序可以在http://www.xxcopy.com方便地使用。它有许多内置选项,专门用于将目录树展平为单个目录。以下将按照您的描述进行操作:

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

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

答案 2 :(得分: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

答案 3 :(得分:1)

为什么在已有管道的情况下使用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

答案 4 :(得分: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" };

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