11

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

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

这个命令至少有两个问题:

  • 它也会复制名称匹配的文件夹*.doc
  • 如果名称冲突,以后找到的任何文件都会通过网络传输并覆盖前一个文件。

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

4

5 回答 5

16

我将首先生成文件列表并在您浏览列表时进行验证。

像这样的东西:

$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目录。

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

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

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

于 2013-07-05T23:10:52.480 回答
7

除非我误解了某些东西,否则以前的答案对我来说似乎过于复杂。这应该有效:

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

于 2013-07-09T18:32:42.493 回答
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
于 2013-07-09T08:04:31.053 回答
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
于 2013-07-13T01:55:27.560 回答
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 必须已经存在)。
一般来说,我建议您将命令拆分为多行,因为它更容易生成代码(在这种情况下,第一个任务:获取文件,第二个任务:复制文件)。

于 2013-07-09T10:03:54.677 回答