【问题标题】:PowerShell copy-item check destination for same file name before copying复制前 PowerShell 复制项检查目标是否具有相同的文件名
【发布时间】:2019-01-17 05:03:33
【问题描述】:

我有一个脚本

  1. 对文件和文件夹进行排序。
  2. 在检查可用空间的同时,以递归方式选择性地将已排序的文件复制到多个位置。
  3. 重命名这些复制文件的扩展名。

脚本运行流畅。但是如果我运行脚本两次,复制部分会复制重复的文件,因为某些扩展名被重命名了。 (问题只发生在重命名的扩展上)

我想不出比在递归和提取基本名称并检查目标中的现有文件时获取每个文件更好的方法。但是有成千上万的文件需要处理。所以效率不高。

目录结构:

  • 主要
  • SUB1
    • 日期1
    • 日期2
    • 日期3
      • 文件夹 1
      • 文件夹2
      • 文件夹 3
      • 文件夹4
        • file_1.extension
        • file_2.extension
        • file_3.extension
  • SUB2

    • 日期1
    • 日期2
    • 日期3
      • category_1
      • category_2
      • category_3
        • sub_cat_1
        • sub_cat_2
        • sub_cat_3
          • file_1.new_extension
          • file_2.new_extension
          • file_3.new_extension
  • 对于每个月的每个日期,我都有其下的每个文件和文件夹。

  • 我将文件从 SUB1 复制到 SUB2

这是我的复制功能之一:

$threshold = 100    
function Copy-1 {

$rmainingSpace = Get-FreeSpace

if($rmainingSpace -gt $threshold)
        {
           $Source = "source\path"

                Copy-Item ($Source) -Destination "destination\path" -Filter "*.extension" -recurse -Verbose 

                Copy-Item ($Source) -Destination "some\other\destination\path" -Filter "*.another_extension" -recurse -Verbose 

            $rmainingSpace = Get-FreeSpace

        }
        else
        {
            Pause($rmainingSpace)
            Copy-1
        }
}
  • 暂停函数暂停脚本,直到按下 Enter。这样,如果磁盘空间用完,我可以清理空间并继续执行脚本的其余部分。
  • 与此类似的其他复制功能很少。我使用多个复制功能,根据文件需要去哪里复制到不同的位置。

如果有人可以提供帮助,非常感谢。 谢谢。

【问题讨论】:

  • 1) 为什么要更改扩展名? 2)你为什么要更改扩展名,使其与第一次更改的不同?似乎没有做 1 或 2 可以解决您的问题。
  • Copy-Item 不检查目标项目是否存在。它只是覆盖了曾经存在的内容......您需要自己检查每个项目 Test-Pathif $false 你告诉 Copy-Item 完成它的工作。这样你也可以安全一段时间。
  • @KoryGill,根据我的要求,我需要更改一些扩展。不改变不是一种选择。
  • @T-Me,好主意。我试试看。

标签: powershell


【解决方案1】:

作为Kory Gill cmets,我也不明白您为什么要更改文件的扩展名。 如果目标文件应该已经存在,我的想法是在文件的基本名称上添加一个序列号。
事实上,如果您手动尝试复制/粘贴已存在的文件,Windows 也建议在文件中添加序列号。

为此,此功能可能很有用:

function Copy-Unique {
    # Copies files to a destination. If a file with the same name already exists in the destination,
    # the function will create a unique filename by appending '(x)' after the name, but before the extension. 
    # The 'x' is a numeric sequence value.
    [CmdletBinding()]
    Param(
        [Parameter(Mandatory = $true, ValueFromPipeline = $true, Position = 0)]
        [Alias("Path")]
        [ValidateScript({Test-Path -Path $_ -PathType Container})]
        [string]$SourceFolder,

        [Parameter(Mandatory = $true, Position = 1)]
        [string]$DestinationFolder,

        [Parameter(Mandatory = $false, Position = 2)]
        [string]$Filter = '*',

        [switch]$Recurse
    )

    # create the destination path if it does not exist
    if (!(Test-Path -Path $DestinationFolder -PathType Container)) {
        Write-Verbose "Creating folder '$DestinationFolder'"
        New-Item -Path $DestinationFolder -ItemType 'Directory' -Force | Out-Null
    }
    # get a list of file FullNames in this source folder
    $sourceFiles = @(Get-ChildItem -Path $SourceFolder -Filter $Filter -File | Select-Object -ExpandProperty FullName)
    foreach ($file in $sourceFiles) {
        # split each filename into a basename and an extension variable
        $baseName  = [System.IO.Path]::GetFileNameWithoutExtension($file)
        $extension = [System.IO.Path]::GetExtension($file)    # this includes the dot

        # get an array of all filenames (names only) of the files with a similar name already present in the destination folder
        $allFiles = @(Get-ChildItem $DestinationFolder -File -Filter "$baseName*$extension" | Select-Object -ExpandProperty Name)
        # for PowerShell version < 3.0 use this
        # $allFiles = @(Get-ChildItem $DestinationFolder -Filter "$baseName*$extension" | Where-Object { !($_.PSIsContainer) } | Select-Object -ExpandProperty Name)

        # construct the new filename
        $newName = $baseName + $extension
        $count = 1
        while ($allFiles -contains $newName) {
            $newName = "{0}({1}){2}" -f $baseName, $count, $extension
            $count++
        }
        # use Join-Path to create a FullName for the file
        $newFile = Join-Path -Path $DestinationFolder -ChildPath $newName
        Write-Verbose "Copying '$file' as '$newFile'"

        Copy-Item -Path $file -Destination $newFile -Force
    }
    if ($Recurse) {
        # loop though each subfolder and call this function again
        Get-ChildItem -Path $SourceFolder -Directory | Select-Object -ExpandProperty Name | ForEach-Object {
            $newSource = (Join-Path -Path $SourceFolder -ChildPath $_)
            $newDestination = (Join-Path -Path $DestinationFolder -ChildPath $_)
            Copy-Unique -SourceFolder $newSource -DestinationFolder $newDestination -Filter $Filter -Recurse
        }
    }
}

我还建议对您的 Copy-1 函数进行一些更改,以使用上述 Copy-Unique 函数:

function Copy-1 {
    [CmdletBinding()]
    Param(
        [Parameter(Mandatory = $true, Position = 0)]
        [Alias("Path")]
        [ValidateScript({Test-Path -Path $_ -PathType Container})]
        [string]$Source,

        [Parameter(Mandatory = $true, Position = 1)]
        [string]$Destination,

        [Parameter(Mandatory = $true, Position = 2)]
        [int]$Threshold,

        [string]$Filter = '*'
    )

    # you are not showing this function, so I have to assume it does what it needs to do
    $remainingSpace = Get-FreeSpace

    if($remainingSpace -gt $Threshold) {
        Copy-Unique -SourceFolder $Source -DestinationFolder $Destination -Filter $Filter -Recurse -Verbose
    }
    else {
        $answer = Read-Host -Prompt "Remaining space is now $remainingSpace. Press 'Q' to quit."
        if ($answer -ne 'Q') {
            # you have cleared space, and want to redo the copy action
            Copy-1 -Source $Source -Destination $Destination -Filter $Filter
        }
    }
}

然后像这样使用它:

Copy-1 -Source 'source\path' -Destination 'destination\path' -Threshold 100 -Filter '*.extension'
Copy-1 -Source 'source\path' -Destination 'some\other\destination\path' -Threshold 100 -Filter '*.another_extension'


注意

当然,使用相同的参数一遍又一遍地运行它,最终会得到很多副本,因为该函数不会比较文件是否相等。如果您想进行真正的文件夹同步,我建议您使用专用软件或使用 RoboCopy。 使用 RoboCopy 进行目录同步的示例在 Internet 上几乎随处可见,例如 here

【讨论】:

  • 添加序列号是个好主意。即使有重复,我也可以轻松识别它们。谢了哥们。也感谢您对我的复制功能的建议。
  • 有时有理由更改文件扩展名...我曾经有一台能够将电影录制到硬盘的电视。它用自己的文件扩展名保存了它们,但它只是一个覆盖的 mp4。我只是更改了所有这些的扩展名,并且可以在 PC 上播放它们。
  • Copy-Unique 是我想要的。我可以修改它以返回复制的文件名。你为我节省了很多精力。
猜你喜欢
  • 2011-12-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-09-23
  • 1970-01-01
  • 2017-07-17
  • 1970-01-01
相关资源
最近更新 更多