Powershell 将文件从源复制到目标但保持文件夹结构

Powershell copy a file from source to target but maintain folder structure

我怎样才能想出一个可以将文件从位置 A 复制到位置 B 并创建新文件夹并维护子目录结构的 powershell 脚本

伪代码

$newFolder = "PackageName"
$maintainFolderStructureFrom ="Website"
$FileToCopy = "File.ascx"

Copy-Item "C:\A\B\Website\SubFolder1\SubFolder2\SubFolder3\"+$FileToCopy "C:\Client\Packages$newFolder" -Container -Recurse

现在它应该这样创建目标

C:\Client\Packages\PackageName\SubFolder1\SubFolder2\SubFolder3\File.ascx

代码基于@jisaak 回答

$newFolder = "NewFolderName"
$FileToCopy="File.ascx"                                                                                                
$pathToCopy = Join-Path 'C:\A\B\Website\SubFolder1\SubFolder2\SubFolder3\' $FileToCopy     
$destination = Join-Path 'C:\Client\Packages\' $newFolder                                                        
mkdir $destination -ErrorAction SilentlyContinue                                                                                      
Copy-Item $pathToCopy $destination  

此代码未创建文件夹结构,我希望创建文件夹结构用户此参数 $maintainFolderStructureFrom ="Website"

首先,您应该使用 Join-Path cmdlet 来组合路径。您可以使用 mkdir 创建目录(如果目录已经存在,使用 -ErrorAction SilentlyContinue 忽略错误):

$pathToCopy = Join-Path 'C:\A\B\Website\SubFolder1\SubFolder2\SubFolder3' $FileToCopy
$destination = Join-Path 'C:\Client\Packages' $newFolder

# create the directory if necessary
mkdir $destination -ErrorAction SilentlyContinue 

Copy-Item $pathToCopy $destination

这里有一些想法:

  • New-Item cmdlet 可以创建文件和文件夹,包括必要的父文件夹(如果它们不存在)。使用 -Force 开关。更重要的是,它将 return 一个 [System.IO.DirectoryInfo] 类型的对象供您使用。
  • 如果将 $pathToCopy 变量设为 [System.IO.FileInfo] 对象,您将获得一些有用的属性和方法,这些属性和方法与使用 Get-Item cmdlet 后可能习惯的其他文件相同.
  • Resolve-Path cmdlet 可用于为您的路径提供一致的格式(例如“\”而不是“/”等),但请注意它需要解析为实际存在的内容。请记住提取该 cmdlet 为您提供的 Path 属性。
  • 有一个很酷的 "Escape" 方法与 [Regex] class 相关联,可以帮助剥离部分文件路径。

下面展示了一些您可能想要或可能不想使用的东西:

$SourceRoot = "C:\A\B\Website"
$SourcePath = "\SubFolder1\SubFolder2\SubFolder3"
$SourceFile = "File.ascx"
$DestRoot = "C:\Client\Packages"
$NewFolder = "PackageName"

# Build a full path to your source file
$FileToCopy = Join-Path -Path $SourceRoot -ChildPath $SourcePath
$FileToCopy = Join-Path -Path $FileToCopy -ChildPath $SourceFile

# Make this a FileInfo object, just because we can :-)
$FileToCopy = $FileToCopy -as [System.IO.FileInfo]

# Or you could have used Get-ChildItem instead:
$FileToCopy = Resolve-Path ($SourceRoot+$SourcePath+$SourceFile)
$FileToCopy = Get-ChildItem -Path $FileToCopy.Path 

# Strip off the "root" folders so we're just left with the relative
# path to the source file from the source root folder
$RelativePath = $FileToCopy.Directory -replace [Regex]::Escape($SourceRoot),''

# Join up the destination path components
$DestFolder = Join-Path $DestRoot $NewFolder
$DestFolder = Join-Path $DestFolder $RelativePath

# Create the target folder if necessary & convert the $DestFolder
# variable into a [System.IO.DirectoryInfo] object!

$DestFolder = New-Item -ItemType Directory -Path $DestFolder -Force

# Copy the file
Copy-Item $FileToCopy $DestFolder

这绝对是一种比您可能想要使用的更长的方法,但我希望该示例包含一些您可以尝试的想法。

类似问题的备选答案:

  • 您可以尝试使用 RoboCopy,根据对此处类似问题的回答:

What's the proper way to copy files while preserving folder structure in powershell?

或在这里查看答案:

Copying files while preserving directory structure

我喜欢其中一些答案,但觉得它们过于冗长,所以我将针对同一问题提供一个较短的答案。

这里的流程是递归抓取源码目录为$source。接下来,遍历它,拉出所有目录,并为每个目录在目标中创建一个新文件夹。

最后再通过$source最后一次把这次的文件拿回来,放到新的位置。

$source = DIR -Path C:\SomeOldPath -Recurse
$destination = 'C:\SomeNewPath'
$source | Where PSIsContainer | ForEach-Object {
    #If the folder doesn't exist in the target
    if(!(Test-Path $Destination$_.Name -PathType Container)){
        New-Item -Path $Destination$_.Name -ItemType directory -Force}
        }
        else {
        Copy-Item $_.FullName -Destination $Destination$_.Name -Force}

$source | Where PSIsContainer -eq $false | ForEach-Object {
    Copy-Item $_.FullName -Destination $Destination$_.Name -Force
    }

在@jisaak 和@FoxDeploy 的一些回答的帮助下,我可以完成我需要的。

function CreatePackage($sourcePath, $fileSelectorToCopy, $packageName) {

$maintainStructionFrom = "Website"

$files = Get-ChildItem $sourcePath | Where-Object {$_.Name -match $fileSelectorToCopy}

$files | ForEach-Object { 
    $newFolder = $packageName
    $fileSelectorToCopy = $_.Name 
    $pathToCopy = Join-Path $sourcePath  $fileSelectorToCopy                                                                                         
    $newFolder = $newFolder+'\'+$pathToCopy.Substring($pathToCopy.IndexOf($maintainStructionFrom),$pathToCopy.Length - $pathToCopy.IndexOf($maintainStructionFrom)).Replace($fileSelectorToCopy,'') 
    $destination = Join-Path 'C:\Client\Packages\' $newFolder  
    mkdir $destination -ErrorAction SilentlyContinue                                                                                      
    Copy-Item $pathToCopy ($destination+$FileToCopy)
}
}

然后称它为

CreatePackage -packageName 'MyNewPackage' -fileSelectorToCopy 'FileNameLike.*' -sourcePath 'C:\A\B\Website\SubFolder1\SubFolder2\SubFolder3\'