当前位置:网站首页 > 更多 > 玩电脑 > 正文

[玩转系统] 使用 PowerShell 合并 SharePoint 中的重复文件夹

作者:精品下载站 日期:2024-12-14 03:40:01 浏览:15 分类:玩电脑

使用 PowerShell 合并 SharePoint 中的重复文件夹


如果我知道在 SharePoint Online 中合并重复文件夹的方法,该博客的一位读者联系了我。用户已经在重复的文件夹中工作过,因此我们不仅需要合并文件夹,还需要保留最后修改的文件。

所有重复的文件或文件夹的文件名都具有相同的模式 (1)。因此,这使得查找文件和文件夹并将它们与原始位置合并变得非常容易。

如果您想知道发生了什么;用户在未先停止同步的情况下在本地删除了文件。由于删除的文件数量很大(超过 750,000 个),他们要求 Microsoft 恢复,他们也这样做了。

仅由于通信错误,恢复作业分两部分完成,导致文件重复。 Microsoft 无法协助清理,所以我介入了。

在这种情况下,第三方备份解决方案确实可以成为救星。我之前写过相关文章;您是否需要 Microsoft 365 的备份解决方案。正如您在本例中所看到的,文件可以恢复,但拥有备份解决方案将使恢复变得更加容易。

查找重复项

合并重复的文件夹和文件将需要几个步骤。重复文件夹不仅位于文档库的根级别,还可以位于子文件夹的 3 层深处。因此,我们需要递归地遍历所有文件夹,查找名称中带有 (1) 的项目。

我已将脚本分解为几个步骤,每个步骤都转换为自己的函数:

  • 查找重复的文件和文件夹
  • 创建目标路径(原始位置)
  • 移动文件夹内容
  • 比较文件日期
  • 移动单个项目

连接到 SharePoint 网站

在我们执行任何操作之前,我们需要连接到 SharePoint 站点。我们可以使用 PnPOnline 简单地完成此操作,并且我在这里使用 Web 登录开关,因此我们可以在这里使用 MFA 进行正常登录。

在文章的最后,我将展示完整的脚本。

# SharePoint url
$sharepointUrl = 'https://a-d.sharepoint.com/'

# Site
$site = 'sites/lab01'

# Library name
$libraryName = 'Duplicates'

# Login 
$url = $sharepointUrl+ '/' + $site
Connect-PnPOnline -Url $url -UseWebLogin

查找重复文件

因为我们还需要处理子文件夹,所以这里的技巧是递归地遍历文件夹。我们在这里使用 Get-PnPFolderItem 函数,但随后以递归方式使用。该函数基于 Josh Einstein 的脚本,您可以在此处找到该脚本。

# Recursively calls Get-PnpFolderItem for a given Document Library
# Based on: https://gist.github.com/josheinstein/3ace0c9f8e25d07583ceb57d13f71b2e

Function Get-PnpFolderItemRecursively($FolderSiteRelativeUrl) {
    
    # Get all items
    $items = @(Get-PnPFolderItem -FolderSiteRelativeUrl $FolderSiteRelativeUrl)

    Foreach ($item in $items) {

        # Strip the Site URL of the item path because Get-PnpFolderItem wants it
        # to be relative to the site, not an absolute path.

        $itemPath = $item.ServerRelativeUrl -replace "^$(([Uri]$item.Context.Url).AbsolutePath)/",''
 
        # Check if the item is a folder
        If ($item -is [Microsoft.SharePoint.Client.Folder]) 
        {
            
            # Check if the folder name contains (1) on the end
            # If - if the folder name contains a (1) on the end, then it's a duplicate folder that we need to move or merge
            # Else - if the folder doesn't contain (1), then we open the folder and search through the next level

            if ($item.name  -like "*(1)") 
            {
         
                # Duplicate folder found
                Write-Host " - Duplicatie folder found: " $itemPath -ForegroundColor Yellow
            
                # Move the content of the folder to the original location
                Move-FolderItemsRecursively($itemPath)
            }
            else
            {
                # Is doesn't contain (1), but it's a folder, search through the next level by recursing into this function.
                Get-PnpFolderItemRecursively $itemPath
            }
        }
        else
        {
            # Item is a file
            # Check if items name contains a (1), if true, move the file

            if ($item.name  -like "*(1)") 
            {
                $targetPath = Create-TargetPath -itemPath $itemPath -targetPath $item["FileRef"].trim("*(1)") -relativePath $relativePath

                Write-Host $newTargetPath;

                Move-CustomItem -SiteRelativeUrl $itemPath -targetPath $targetPath -item $item
            }
            # Else skip to next
        }
    }
}

因此,对于每个项目,我们都会检查它是否是一个文件夹。当它是一个文件夹时,我们检查文件夹名称是否包含(1)。如果是,我们将移动文件夹内容,如果不是,我们“打开”文件夹并浏览内容。

如果它不是文件夹,而是文件,那么我们再次检查名称。如果它包含 (1),那么我们将移动该文件,否则我们什么都不做。

创建目标路径

如果我们发现重复的项目,我们需要从该项目的路径创建原始路径。我们还需要检查原始文件夹是否存在,如果不存在则重新创建原始文件夹。

Function Create-TargetPath {
    [CmdletBinding()]
    param(
         [parameter (Mandatory=$true)]
         $itemPath,

         [parameter (Mandatory=$true)]
         $item,

         [parameter (Mandatory=$false)]
         $relativePath
     )

    process
	{
        # Build new path
        $path = $itemPath.replace($item.name,'') 
        $targetPath = "/" + $site + "/" + $path + $item.name

        if ($whatIf -ne $true)
        {
            # Check if target folder exists, create if necessary
            Write-host ' - Check if target folder exists' $path.replace('(1)', '') -BackgroundColor DarkMagenta;
            $result = Resolve-PnPFolder -SiteRelativePath $path.replace('(1)', '') -ErrorAction SilentlyContinue
        }
        else{
            Write-host ' - Create target folder if it does not exists' $path.replace('(1)', '') -BackgroundColor DarkMagenta;
        }

        Write-Output $targetPath.replace('(1)', '')
    }
}

您将在每个函数中看到我正在使用变量 $whatIf。在脚本的开头,我声明了这个变量。这使我可以测试脚本,而无需实际移动或创建任何文件或文件夹。

大多数 PnP 函数确实支持 WhatIf 开关,但例如 Resolve-PnPFolder 不支持它。这样我就可以通过向控制台写入脚本将执行的操作来简单地测试它。

create-targetPath 函数将重新创建路径,检查文件夹是否存在,如果不存在,则使用 Resolve-PnPfolder 函数重新创建它。

使用 PowerShell 移动文件夹和子文件夹

当我们找到重复的文件夹时,我们希望将该文件夹和所有内容(包括子文件夹)移动到原始位置。对于每个子文件夹,我们还需要创建一个目标路径。

Function Move-FolderItemsRecursively($FolderSiteRelativeUrl) {

    # Get all items in this sub folder
    $items = @(Get-PnPFolderItem -FolderSiteRelativeUrl $FolderSiteRelativeUrl)

    foreach ($item in $items) {

        # Strip the Site URL off the item path, because Get-PnpFolderItem wants it
        # to be relative to the site, not an absolute path.
        
        $itemPath = $item.ServerRelativeUrl -replace "^$(([Uri]$item.Context.Url).AbsolutePath)/",''

        # If this is a directory, recurse into this function.
        # Otherwise, build target path and move file

        if ($item -is [Microsoft.SharePoint.Client.Folder]) 
        {
            Move-FolderItemsRecursively $itemPath
        }
        else 
        {
            $targetPath = Create-TargetPath -itemPath $itemPath -item $item
            
            Move-CustomItem -SiteRelativeUrl $itemPath -targetPath $targetPath -item $item
        }
    }
}

比较 SharePoint Online 中的文件日期并移动文件

现在我们来到了最重要的部分,移动实际文件。在移动文件之前,我们需要检查文件是否存在于原始位置。如果是这样,我们需要比较文件日期,在这种情况下我们希望保留最后修改的文件。

# Move file to original folder
Function Move-CustomItem  {
    [CmdletBinding()]
    param(
         [parameter (Mandatory=$true)]
         $siteRelativeUrl,

         [parameter (Mandatory=$true)]
         $targetPath,

         [parameter (Mandatory=$true)]
         $item
     )

    process
	{
        $moveFile = Compare-FileDates -sourceFilePath $siteRelativeUrl -targetFilePath $targetPath;
		$global:moveLimitCounter++

        if ($moveFile -eq $true) 
        {

			if ($moveLimitCounter -eq $moveLimit)
			{
				Write-Warning 'Move limit reached'
				exit;	
			}

            if ($whatIf -ne $true)
            {
				# Move the file
				Write-host '   - Move item to' $targetPath -BackgroundColor DarkYellow;
				Move-PnPFile -SiteRelativeUrl $siteRelativeUrl -TargetUrl $targetPath -OverwriteIfAlreadyExists -Force:$force
				Write-Host "`r`n"
				
            }
            else
            {
                Write-host '   - Move file from' $siteRelativeUrl -BackgroundColor DarkCyan
				Write-host '     to' $targetPath -BackgroundColor DarkCyan
				Write-Host "`r`n"
            }
        }
    }    
}

我在这里添加了一个计数器,用于计算移动的文件数量。当您要测试这样的脚本时,您可能只想先移动几个文件,然后在继续之前检查结果。

# Check if the file already exists in the target location
# If the file exists, we need to compare the dates to keep the latest files

Function Compare-FileDates () 
{
    [CmdletBinding()]
    param(
         [parameter (Mandatory=$true)]
         $targetFilePath,

         [parameter (Mandatory=$true)]
         $sourceFilePath
     )

    $targetFileExists = Get-PnPFile -Url $targetFilePath -ErrorAction SilentlyContinue
    
    If($targetFileExists)
    {
        $sourceFile = Get-PnPFile -Url $sourceFilePath -AsListItem
        $targetFile = Get-PnPFile -Url $targetFilePath -AsListItem

        $sourceFileDate = Get-date $sourceFile['Modified']
        $targetFileDate = Get-date $targetFile['Modified']

        write-host ' - Comparing files dates: duplicate file: '$sourceFileDate 'original file: '$targetFileDate

        # Check if the source file is newer then the target file
        If ($sourceFile['Modified'] -gt $targetFile['Modified']) 
        {
            write-host '    - Duplicate file is newer, move the file' -BackgroundColor DarkGreen
            write-output $true
        }
        else
        {
			# Remove file
			if ($whatIf -ne $true)
            {
				Write-host '    - Target file is newer. Removing duplicate file' -BackgroundColor DarkRed
				Write-Host "`r`n"
				Remove-PnPFile -SiteRelativeUrl $sourceFilePath -Recycle -Force:$force
			}
			else
			{
				Write-Host 'Remove file' $sourceFilePath  -ForegroundColor Red
				Write-Host "`r`n"
			}
            write-output $false
        }
    }
    else
    {
        # Target file doesn't exists
        Write-host ' - Target file does not exist' -BackgroundColor DarkGreen
        Write-Output $true
    }
}

因此,我们比较日期,如果原始文件较新,则我们删除重复的文件。在删除文件功能中,我添加了开关-Recycle,这样我们的文件就会被移动到回收站。如果您想永久删除文件,可以删除此开关。

总结

很高兴知道您的库中可能会出现一些空文件夹。我使用了一个单独的脚本,您可以运行它来清理空文件夹。您可以在我的 GitHub 上找到完整的脚本。

始终使用一些测试文件在测试 SharePoint 网站上测试这些类型的脚本。开始时,请确保将 whatif 设置为 $true,将变量 force 设置为 $false

如果您有任何疑问,请在下面发表评论。

您需要 登录账户 后才能发表评论

取消回复欢迎 发表评论:

关灯