Powershell 2中的copy-item命令,如果目标文件夹不存在,则会创建一个新的文件夹。

78
$from = "\\something\1 XLS\2010_04_22\*"
$to =  "c:\out\1 XLS\2010_04_22\"
copy-item $from $to -Recurse 

如果 c:\out\1 XLS\2010_04_22\ 存在,则此方法有效。是否可能用单个命令创建 c:\out\1 XLS\2010_04_22\,如果它不存在?


这需要使用PowerShell cmdlet吗,还是您可以使用xcopy,它在PowerShell提示符中可用,但不是PowerShell cmdlet? - Shiraz
11个回答

120

在PowerShell 2.0中,仍然无法让Copy-Item cmdlet创建目标文件夹,您需要使用以下代码:

$destinationFolder = "C:\My Stuff\Subdir"

if (!(Test-Path -path $destinationFolder)) {New-Item $destinationFolder -Type Directory}
Copy-Item "\\server1\Upgrade.exe" -Destination $destinationFolder
如果您在Copy-Item命令中使用-Recurse参数,它将在目标中创建源结构的所有子文件夹,但它不会创建实际的目标文件夹,即使使用了-Force参数也是如此。

感谢您的观察。我发现复制操作有时会不一致,有时会复制子文件夹,有时则不会。在确保目标文件夹存在后,这种不一致性就消失了。 - Joost van der Griendt
2
当目标文件夹不存在时,我也遇到了错误的行为。 - vicancy
2
谢谢。这正是我在寻找的答案。可惜没有开关可以创建目标文件夹。 - jdawiz

87

是的,添加 -Force 参数。

copy-item $from $to -Recurse -Force

5
当一个人掌握PowerShell时,它非常容易 :) - stej
87
那个方法适用于整个目录,但如果我只是复制单个文件(以便我可以查看状态),该怎么办?当c:\test2不存在时,如何将c:\test1\file.txt复制到c:\test2\file.txt?这才是我真正需要知道的。谢谢,Gary - Gary Evans
52
我遇到了同样的问题,通过先调用New-Item解决了它: New-Item -Force $to Copy-Item -Force $from $to - rjschnorenberg

20

在PowerShell 3及以上版本中,我使用Copy-Item和New-Item。

copy-item -Path $file -Destination (new-item -type directory -force ("C:\Folder\sub\sub\" + $newSub)) -force -ea 0

我在2版本中尚未尝试过。


这对于本地复制非常好,谢谢!但是在使用“-ToSession”命令时不起作用:'( - Hicsy

3
function Copy-File ([System.String] $sourceFile, [System.String] $destinationFile, [Switch] $overWrite) {

    if ($sourceFile -notlike "filesystem::*") {
        $sourceFile = "filesystem::$sourceFile" 
    }

    if ($destinationFile -notlike "filesystem::*") {
        $destinationFile = "filesystem::$destinationFile" 
    }

    $destinationFolder = $destinationFile.Replace($destinationFile.Split("\")[-1],"")

    if (!(Test-Path -path $destinationFolder)) {
        New-Item $destinationFolder -Type Directory
    }

    try {
        Copy-Item -Path $sourceFile -Destination $destinationFile -Recurse -Force
        Return $true 
    } catch [System.IO.IOException] {
        # If overwrite enabled, then delete the item from the destination, and try again:
        if ($overWrite) {
            try {
                Remove-Item -Path $destinationFile -Recurse -Force        
                Copy-Item -Path $sourceFile -Destination $destinationFile -Recurse -Force 
                Return $true
            } catch {
                Write-Error -Message "[Copy-File] Overwrite error occurred!`n$_" -ErrorAction SilentlyContinue
                #$PSCmdlet.WriteError($Global:Error[0])
                Return $false
            }
        } else {
            Write-Error -Message "[Copy-File] File already exists!" -ErrorAction SilentlyContinue
            #$PSCmdlet.WriteError($Global:Error[0])
            Return $false
        }
    } catch {
        Write-Error -Message "[Copy-File] File move failed!`n$_" -ErrorAction SilentlyContinue
        #$PSCmdlet.WriteError($Global:Error[0]) 
        Return $false
    } 
}

1
欢迎来到Stack Overflow!感谢您提供这段代码片段,它可能会提供一些即时帮助。通过展示为什么这是一个好的解决方案,适当的解释将极大地提高其教育价值,并使其对未来具有类似但不完全相同问题的读者更有用。请编辑您的答案以添加解释,并指出适用的限制和假设。 - Toby Speight

3
  $filelist | % {
    $file = $_
    mkdir -force (Split-Path $dest) | Out-Null
    cp $file $dest
  } 

3
$file | Copy-Item -Destination (New-Item -Force -Type Directory -Path $directoryName)

3
这是一个对我有用的示例。我有一个包含大约500个特定文件的列表,这些文件在约100个不同的文件夹中,并且如果以后需要这些文件,我需要将它们复制到备份位置。文本文件每行包含完整的路径和文件名。在我的情况下,我想要从每个文件名中剥离驱动器字母和第一个子文件夹名称。我想要将所有这些文件复制到我指定的另一个根目标文件夹下的类似文件夹结构中。我希望其他用户会发现这有用。
# Copy list of files (full path + file name) in a txt file to a new destination, creating folder structure for each file before copy
$rootDestFolder = "F:\DestinationFolderName"
$sourceFiles = Get-Content C:\temp\filelist.txt
foreach($sourceFile in $sourceFiles){
    $filesplit = $sourceFile.split("\")
    $splitcount = $filesplit.count
    # This example strips the drive letter & first folder ( ex: E:\Subfolder\ ) but appends the rest of the path to the rootDestFolder
    $destFile = $rootDestFolder + "\" + $($($sourceFile.split("\")[2..$splitcount]) -join "\")
    # Output List of source and dest 
    Write-Host ""
    Write-Host "===$sourceFile===" -ForegroundColor Green
    Write-Host "+++$destFile+++"
    # Create path and file, if they do not already exist
    $destPath = Split-Path $destFile
    If(!(Test-Path $destPath)) { New-Item $destPath -Type Directory }
    If(!(Test-Path $destFile)) { Copy-Item $sourceFile $destFile }
}

2

我最喜欢使用 .Net [IO.DirectoryInfo] 类,它会处理一些逻辑。实际上,我在很多类似的脚本挑战中都使用它。它有一个 .Create() 方法,可以创建不存在的目录,如果目录已经存在也不会出错。

由于这仍然是一个两步问题,我使用 foreach 别名使其简单化。对于单个文件:

[IO.DirectoryInfo]$to |% {$_.create(); cp $from $_}

就您的多文件/目录匹配而言,我建议使用RoboCopy而不是xcopy。从from中删除“ *”,只需使用:

RoboCopy.exe $from $to *

你仍然可以添加 /r(递归)、/e(包括空文件夹的递归),还有其他 50 个有用的开关。
编辑:回顾一下,如果你不经常使用这段代码,它虽然简洁,但并不容易阅读。通常我会将其分成两部分,如下所示:
([IO.DirectoryInfo]$to).Create()
cp $from $to

另外,DirectoryInfoFileInfoParent属性的类型,所以如果你的$to是一个文件,你可以一起使用它们:

([IO.FileInfo]$to).Parent.Create()
cp $from $to

1

我曾两次在这里跌倒,最近一次是一个独特的情况,尽管我放弃了使用copy-item,但我仍想发布我使用的解决方案。

我有一个文件列表,只有完整路径和大多数情况下没有扩展名。对于我来说,-Recurse -Force选项无法工作,因此我放弃了copy-item函数,并回退到像下面这样使用xcopy,因为我仍然希望保持它成为一个单行代码。起初我试过Robocopy,但显然它正在寻找文件扩展名,由于我的许多文件没有扩展名,所以它将其视为目录。

$filelist = @("C:\Somepath\test\location\here\file","C:\Somepath\test\location\here2\file2")

$filelist | % { echo f | xcopy $_  $($_.Replace("somepath", "somepath_NEW")) }

希望能帮到有需要的人。

0
我修改了@FrinkTheBrave的答案,使其也能创建子目录并解析相对路径:
$src_root = Resolve-Path("./source/path/")
$target_root = Resolve-Path("./target/path/")
$glob_filter = "*.*"
Get-ChildItem -path $src_root -filter $glob_filter -recurse | 
  ForEach-Object {
        $target_filename=($_.FullName -replace [regex]::escape($src_root),$target_root) 
        $target_path=Split-Path $target_filename
        if (!(Test-Path -Path $target_path)) {
            New-Item $target_path -ItemType Directory
        }
        Copy-Item $_.FullName -destination $target_filename
    }

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