需要将平面目录与路径+文件的文本文件列表进行比较,并从文本文件复制到具有路径结构的新位置

时间:2015-12-19 18:39:57

标签: windows powershell powershell-v3.0

我正在尝试编写一个脚本,将大型共享的文件结构保存到文本文件中,其中每行是文件夹或路径+文件,并从已提取的大量文件中重建此结构保存在平面文件夹中。所以我想搜索文本文件,当我找到平面文件夹中的匹配项时,我想将该文件从平面文件夹中保存到新位置,但保留文件所在位置的文件夹结构(在文本中)文件)。如果文件在结构中多次(在文件夹结构中的其他文件夹中重复),我真的不在乎。

例如:我在原始结构的文本文件($outfile)中有以下项目:

H:\
H:\a.doc
H:\b\c\d.pdf
H:\b\c\e\f\g.xls

然后我

a.doc
d.pdf
g.xls

所有在没有文件夹($extractpath)的平面文件夹中,我想将它们复制到类似

的文件夹中
I:\a.doc
I:\b\c.pdf
I:\b\c\e\f\g.xls

所以我需要

  1. 搜索$outfile c.doc
  2. 然后从H:\b\
  3. 中提取路径H:\b\c.doc
  4. c.doc复制到I:\b\
  5. 鉴于$outfile对于每行文本文件(27m)来说有点大,我希望有一种优雅的方法来执行一次目录结构编写,然后将文件读入内存并检查每个文件名在$extractpath中,从$filepath中提取$outfile,然后将此文件复制到$filepath

    这是我到目前为止所做的事情(尚未开展工作):

    # Path to log file date/timestamped
    $logfile = "D:\Shares\Extracted\_extracted_2_$(Get-Date -format 'yyyyMMdd_hhmm').log"
    
    # Path of root directory
    $rootfolder = "H:\"
    
    # Text file with Structure of the root directory, recursively
    $outfile = "D:\Shares\Extracted\__struct.txt"
    
    # Path of the files that were previously extracted with extract.ps1
    $extractpath = "D:\Shares\Extracted"
    
    # Path to move files to
    $newpath = "D:\Shares\New"
    
    if (!(Test-Path $outfile -PathType Leaf)) {
      Get-ChildItem $rootfolder | select -Expand fullname > $outfile
      Get-ChildItem $rootfolder -recurse | select -Expand fullname > $outfile
    } else {
      Write-Output "File $outfile exists.. Reading from it.."
    }
    
    $files = get-childitem $extractpath
    foreach ($file in $files) {
      $filepath  = $null
      $filepaths = (Get-Content -Path $outfile | Where-Object { $_ -like '*$filename'}) 
      foreach ($filepath in $filepaths){ 
        if ($filepath) {
          Copy-Item $file -Destination "$newpath\$filename"
        } else {
          Write-Output "Not found: $filename..."
        }
      }
    }
    
    编辑:感谢下面的Ansgar Wiechers,我不是最多15个帖子所以我还不能投票给你。这就是我创建的并最终在我的测试数据上完美运行(我的testpath()函数目前已被破坏,因此忽略了我在那里的范围问题)..

    $outfile   = "d:\Shares\Extracted\__struct.txt"
    $outfolder = "d:\Shares\Extracted\__folder.txt"
    $extract   = "d:\Shares\Extracted"
    $oldpath   = "H:\"
    $newpath   = "d:\newH\"  #Make sure this has trailing backslash
    $multfile  = "d:\newH\__multiple.log"
    $logfile   = "d:\newH\__ext2_$(get-date -format `"yyyyMMdd_hhmm`").log"
    
    $tpok=$false
    
    function log($string, $color)
    {
      if ($Color -eq $null) {$color = "white"}
      write-host $string -foregroundcolor $color
      $string | out-file -Filepath $logfile -append
    
    }
    
    function multlog($string, $color)
    {
      if ($Color -eq $null) {$color = "white"}
      write-host $string -foregroundcolor $color
      $string | out-file -Filepath $logfile -append   # put in both log files     but output to screen only once
      $string | out-file -Filepath $multfile -append
    
    }
    
    
    function testpath($tpok)
    {
    $outpath = Split-Path -Path $outfile
    $logpath = Split-Path -Path $logfile
    $multpath = Split-Path -Path $multfile
    if (!(Test-Path $outpath)) { log "bad Output path:  $outfile" red  return     $false }
    if (!(Test-Path $multpath)) { log "bad Multiples path:  $multpath" red return $false }
    if (!(Test-Path $extract)) { log "bad Extract path:     $extract" red return     $false }
    if (!(Test-Path $oldpath)) { log "bad Oldpath path:     $oldpath" red return $false }
    if (!(Test-Path $newpath)) { log "bad Newpath:      $newpath" red return $false }
    if (!(Test-Path $logpath)) { log "bad Logfile:      $logfile" red return $false}
    log "Paths test good.." yellow
    $tpok = $true
    return $true
    }
    
    function buildstructfiles()
    {
      $testoutfile=(Test-Path $outfile -PathType Leaf)
      if (!($testoutfile))  {
        log "Writing $outfile.."  yellow
        log "Files in the root only.." yellow
        get-childitem $oldpath | where {!($_.psiscontainer)} | select -expand fullname > $outfile |
        log "Recursive directory export.." yellow
        get-childitem $oldpath -recurse | where {!($_.psiscontainer)} | select -expand fullname >> $outfile
        log "Done." yellow
      } else {
        log "File $outfile exists.. Reading from it.." yellow  
      }
      $testoutfolder=(Test-Path $outfolder -PathType Leaf)
      if (!($testoutfolder))  {  
        log "Writing $outfolder.."  yellow
        log "Folders in the root only.." yellow
        get-childitem $oldpath | where {($_.psiscontainer)} | select -expand fullname > $outfolder
        log "Folders recursively.." yellow
        get-childitem $oldpath -recurse | where {($_.psiscontainer)} | select -expand fullname >> $outfolder
      } else {
        log "File $outfolder exists.. Reading from it.." yellow  
      }
    }
    
    function Main()
    {
    
      $destination = @{}
        if (!(testpath)) { Exit }
      buildstructfiles
    
      # Read full path+filenames from $outfile and create new destinations for each file in array $destination
      log "$(get-date -format `"yyyyMMdd_HHmm`") Reading $outfile check and writing $newpath destinations.. " yellow
      Get-Content $outfile | Where-Object {
        $_ -notmatch '\\$' # remove folders (lines with trailing backslash)
      } | ForEach-Object {
        $filename = Split-Path -Leaf $_
        log "Filename:  $filename"
        $chgoldpath = Split-Path -Path $_            # take path of current     object
        $chg = $chgoldpath -replace [regex]::Escape($oldpath), $newpath        #replace $oldpath with $newpath
        if ($destination.Contains($filename)) {
          # it has already been found.  Log this in a separate file as well for later comparison
          multlog "$filename has already been found in $($destination[$filename]), now in $chgoldpath" red
        }
        $destination[$filename]=$chg              #save this into an array
        log "New Destination: $($destination[$filename])"
        log "----"
      }
    
      #  create directory structure from saved txt file $outfolder
      log "$(get-date -format `"yyyyMMdd_HHmm`") Create folders in new structure     $newpath" red
      Get-Content $outfolder | ForEach-Object {
        log "object: $_"
        $d = ($_ -replace [regex]::Escape($oldpath), $newpath)
        log "New Directory to be created: $d" yellow
        if (!(Test-Path($d))) { 
          new-item $d -itemtype directory
        } else {
          log "Directory already exists! $d" gray
        }
      }
    
    #  Or use robocopy to replicate folder structure  
    #  log "$(get-date -format `"yyyyMMdd_HHmm`") Starting folder structure     Robocopy.." yellow
    #  robocopy $oldPath $newPath /e /xf /LOG+:$logFile
    
      # Copy the files from $extractpath to new $destination
      Get-ChildItem $extract | ForEach-Object {
        $dname = Split-Path -Leaf $_
        $dfolder = Split-Path -Path $_
        if ($destination.Contains($dname)) {
          log "$($dname): Destination $($destination[$dname])" darkcyan
          Copy-Item $_ -Destination ($destination[$dname])
        } else {
          log "$($dname) does not exist in $oldpath, not restored." red
        }
      }
    }
    
    Main 
    

1 个答案:

答案 0 :(得分:0)

假设您的所有文件名都是唯一的(因为它们位于单个文件夹中),您可以将目标文件读入哈希表,将每个文件名映射到其目标路径:

$outfile   = 'D:\Shares\Extracted\__struct.txt'
$rootdrive = 'H:'
$newpath   = 'D:\Shares\New'

$destination = @{}
Get-Content $outfile | Where-Object {
  $_ -notmatch '\\$' # remove folders (lines with trailing backslash)
} | ForEach-Object {
  $filename = Split-Path -Leaf $_
  $destination[$filename] = $_ -replace "^$rootdrive", $newpath
}

如果您需要(重新)创建丢失的文件夹(原始问题没有提及),您可以在同一步骤中执行此操作:

$destination = @{}
Get-Content $outfile | Where-Object {
  $_ -notmatch '\\$' # remove folders (lines with trailing backslash)
} | ForEach-Object {
  $path     = $_ -replace "^$rootdrive", $newpath
  $filename = Split-Path -Leaf $path
  $folder   = Split-Path -Parent $path

  $destination[$filename] = $path

  if (-not (Test-Path -PathType Container -LiteralPath $folder)) {
    New-Item -Type Directory $folder | Out-Null
  }
}

然后您可以像这样复制$extractpath中的文件:

$extractpath = 'D:\Shares\Extracted'

Get-ChildItem $extractpath | Copy-Item -Destination {$destination[$_.Name]}