为什么Copy-Item默认情况下会覆盖目标文件?

21
以下是我期望下面代码的行为:
  1. 获取源目录中文件列表。

  2. 循环遍历每个文件,仅在备份目标不存在该文件时才复制。

  3. if (!(Test-Path C:\Folder\Destination)) {
       New-Item -ItemType Directory -Force -Path C:\Folder\Destination
    }
    
    $originalfiles = Get-ChildItem -Path  "C:\Folder\Source"
    $originalfiles
    
    foreach ($file in $originalfiles) {
        Write-Host
        Write-Host File Name: -ForegroundColor DarkYellow
        Write-Host $file.Name
        Write-Host File Path: -ForegroundColor DarkYellow
        Write-Host $file.FullName
    
        $src = $file.FullName
        $dest = "C:\Folder\Destination\$($file.Name)"
    
        Copy-Item $src $dest
    }
    
    我本以为Copy-Item cmdlet默认情况下不会覆盖,除非您指定-Force标志。这是我过去遇到需要覆盖的情况时看到的行为。 此外,我认为可能是因为介绍了foreach循环,但我尝试了单独使用带有硬编码路径的复制命令,结果还是一样的。 我应该重新启动IDE,还是我忽略了什么错误?
6个回答

29

如有疑问,请阅读文档

-Force

表示此命令将复制无法更改的项目,例如复制只读文件或别名。

Copy-Item的默认行为是替换现有项目。只有当目标文件具有只读属性时,-Force开关才会强制替换,例如。

您可以使用-ConfirmCopy-Item执行操作之前进行提示,或者您可以使用-WhatIf查看此命令会执行的操作。


文档引用的措辞实际上表明-Force影响复制的(源)项目,而不是目标项目。您是在说这里-Force适用于目标项目吗?如果是这样,我们可以提交一个拉取请求以更新该文档。 - TylerH

17

虽然老旧,但我找到了一个简短的表达方式:

Copy-Item (Join-Path $dir_from "*") $dir_to -Exclude (Get-ChildItem $dir_to)

它会复制$dir_from中的所有文件到$dir_to,但不会复制已经存在于$dir_to中的文件(-Exclude部分)。


这是我在复制和覆盖同名现有文件方面使用过的唯一有效方法。对于不熟悉PowerShell的人来说,$dir_from和$dir_to应该用引号定义。例如,$dir_to =“C:\ users \ ...” - Sarah
5
如果有人想要使用“-recurse”,那么这将没有用处。 - Appleoddity

5

Copy-Item 的预期行为似乎是将项目复制到目标位置,即使该项目已经存在于目标位置。我建议先测试目标文件路径是否存在,仅在文件不存在时再复制文件。

$destinationPath = 'C:\tryout\destination';
if (!(Test-Path $destinationPath)) 
{
   New-Item -ItemType Directory -Force -Path $destinationPath;
}

$sourcePath = 'C:\tryout\source';
$originalfiles = Get-ChildItem -Path $sourcePath;
$originalfiles;

foreach ($file in $originalfiles) 
{
    Write-Host;
    Write-Host File Name: -ForegroundColor DarkYellow;
    Write-Host $file.Name;
    Write-Host File Path: -ForegroundColor DarkYellow;
    Write-Host $file.FullName;

    $src = $file.FullName;
    $dest = "C:\tryout\destination\$($file.Name)";

    if (!(Test-Path $dest))
    {
        Copy-Item $src -Destination $dest;
    }
}

3
当有人想要使用“-recurse”时,这并未考虑在内。 - Appleoddity

3
这是一个用于复制文件的代码版本,它是递归地复制文件,只有在目标位置不存在时才进行。如果需要,它会创建子文件夹。它不会创建空文件夹;如果需要这样做,请从Get-ChildItem中删除-File
$srcDirPath = "C:\SourceDir"
$destDirPath = "C:\DestinationDir"
# Get list of files from source dir recursively. It doesn't list hidden files by default 
# Remove where{} if you don't need to exclude files by name, in this sample I exclude all README.md in any folders
$fileList = Get-ChildItem -Path $srcDirPath -File -Recurse | where{$_.Name -ne 'README.md'}
# get source dir path just in case to exactly match Get-ChildItem paths
$fullSrcDirPath = (Get-Item -Path $srcDirPath).FullName
foreach($file in $fileList)
{
    # build full destination file path
    $destFilePath = $destDirPath + $file.FullName.Remove(0, $fullSrcDirPath.Length)
    # proceed only if file doesn't exist in destination
    if(-not (Test-Path "$destFilePath"))
    {
        # Copy-Item can't create subfolders, so create subfolder if it doesn't exist
        # Get full destination folder path for current file
        $curDestDir = Split-Path $destFilePath -Parent
        if(-not (Test-Path "$curDestDir"))
        {
            # create subfolder(s)
            # $res here suppresses output
            $res = New-Item -Path "$curDestDir" -ItemType "directory"
        }
        Copy-Item -Path $file.FullName -Destination "$destFilePath"
    }
}

1

无法编辑@Vasiliy Zverev的答案。我将其制作成一个函数,并根据相对文件模式提供了排除列表。与批准的答案相比,好处在于它可以递归执行。

function CpNoClobber ($srcDirPath,$destDirPath,$exclude)
{
    $fileList = Get-ChildItem -Path $srcDirPath  -File -Recurse 
# get source dir path just in case to exactly match Get-ChildItem paths
        $fullSrcDirPath = (Get-Item -Path $srcDirPath).FullName
        foreach($file in $fileList)
        {
            $rel= $file.FullName.Remove(0, $fullSrcDirPath.Length+1)
            $toexc= $exclude | %{ $rel -like  $_ } | Select-Object -First 1
            if ($toexc)
            {
                continue
            }
# build full destination file path
            $destFilePath = $destDirPath + $file.FullName.Remove(0, $fullSrcDirPath.Length)
# proceed only if file doesn't exist in destination
                if(-not (Test-Path "$destFilePath"))
                {
# Copy-Item can't create subfolders, so create subfolder if it doesn't exist
# Get full destination folder path for current file
                    $curDestDir = Split-Path $destFilePath -Parent
                        if(-not (Test-Path "$curDestDir"))
                        {
# create subfolder(s)
# $res here suppresses output
                            $res = New-Item -Path "$curDestDir" -ItemType "directory"
                        }
                    Copy-Item -Path $file.FullName -Destination "$destFilePath"
                    echo $file.FullName
                }
        }

}

使用示例:

CpNoClobber c:\temp c:\temp2 @("test\*")

0

如果你担心覆盖问题,可以使用以下快捷方式:

function mycopy { 
  if (! (test-path $args[1])) { copy $args[0] $args[1] } else { 'file exists' } 
}

1
这个答案如果能解释一下代码的作用以及如何解决 OP 的问题会更有帮助。 - TylerH

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