รวมโฟลเดอร์ที่ซ้ำกันใน SharePoint กับ PowerShell
ผู้อ่านบล็อกนี้ติดต่อฉันหากฉันรู้วิธีรวมโฟลเดอร์ที่ซ้ำกันใน SharePoint Online ผู้ใช้ได้ทำงานในโฟลเดอร์ที่ซ้ำกันแล้ว ดังนั้นเราจึงไม่เพียงแต่จำเป็นต้องรวมโฟลเดอร์เท่านั้น แต่ยังต้องเก็บไฟล์ที่แก้ไขล่าสุดไว้ด้วย
ไฟล์หรือโฟลเดอร์ที่ซ้ำกันทั้งหมดมีรูปแบบเดียวกัน (1) ในชื่อไฟล์ ซึ่งทำให้การค้นหาไฟล์และโฟลเดอร์และรวมเข้ากับตำแหน่งเดิมเป็นเรื่องง่ายมาก
ในกรณีที่คุณสงสัยว่าเกิดอะไรขึ้น ผู้ใช้ลบไฟล์ในเครื่องโดยไม่หยุดการซิงค์ก่อน เนื่องจากมีไฟล์จำนวนมากที่ถูกลบ (มากกว่า 750,000 ไฟล์) พวกเขาจึงขอให้ Microsoft กู้คืนไฟล์ ซึ่งพวกเขาก็ทำไปแล้ว
เนื่องจากข้อผิดพลาดในการสื่อสาร งานการกู้คืนจึงดำเนินการเป็นสองส่วน ส่งผลให้มีไฟล์ที่ซ้ำกัน Microsoft ไม่สามารถช่วยเหลือในการล้างข้อมูลได้ ดังนั้นฉันจึงเข้ามา
นี่เป็นหนึ่งในสถานการณ์ที่โซลูชันสำรองข้อมูลของบริษัทอื่นสามารถช่วยชีวิตได้จริงๆ ฉันเคยเขียนเกี่ยวกับเรื่องนี้มาก่อน คุณต้องการโซลูชันการสำรองข้อมูลสำหรับ Microsoft 365 หรือไม่ ดังที่คุณเห็นในกรณีนี้ ไฟล์ต่างๆ สามารถกู้คืนได้ แต่การมีโซลูชันการสำรองข้อมูลจะทำให้การกู้คืนง่ายขึ้นมาก
การค้นหารายการที่ซ้ำกัน
การรวมโฟลเดอร์และไฟล์ที่ซ้ำกันจะใช้เวลา 2-3 ขั้นตอน โฟลเดอร์ที่ซ้ำกันไม่เพียงแต่อยู่ที่ระดับรูทของไลบรารีเอกสารเท่านั้น แต่ยังสามารถลึกได้ถึง 3 ระดับในโฟลเดอร์ย่อยอีกด้วย ดังนั้นเราจึงต้องทำงานแบบวนซ้ำในโฟลเดอร์ทั้งหมด โดยค้นหารายการที่มี (1) อยู่ในชื่อ
ฉันได้แบ่งสคริปต์ออกเป็นสองขั้นตอน แต่ละขั้นตอนแปลเป็นฟังก์ชันของตัวเอง:
- ค้นหาไฟล์และโฟลเดอร์ที่ซ้ำกัน
- สร้างเส้นทางเป้าหมาย (ตำแหน่งเดิม)
- ย้ายเนื้อหาของโฟลเดอร์
- เปรียบเทียบวันที่ของไฟล์
- ย้ายรายการเดียว
เชื่อมต่อกับไซต์ SharePoint
ก่อนที่เราจะดำเนินการใดๆ เราจำเป็นต้องเชื่อมต่อกับไซต์ SharePoint ก่อน เราสามารถทำได้ง่ายๆ ด้วย PnPOnline และฉันใช้สวิตช์การเข้าสู่ระบบเว็บที่นี่ เพื่อให้เราสามารถใช้การเข้าสู่ระบบปกติของเรากับ MFA ที่นี่
ในตอนท้ายของบทความ ฉันจะแสดงสคริปต์ทั้งหมด
# SharePoint url
$sharepointUrl = 'https://lazyadmin.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
}
}ดังนั้นเราจึงเปรียบเทียบวันที่ และหากไฟล์ต้นฉบับใหม่กว่า เราก็จะลบไฟล์ที่ซ้ำกันออก ในฟังก์ชันการลบไฟล์ ฉันเพิ่มสวิตช์ -รีไซเคิล เพื่อให้ไฟล์ถูกย้ายไปยังถังรีไซเคิล คุณสามารถลบสวิตช์นี้ได้หากต้องการลบไฟล์อย่างถาวร
ห่อขึ้น
เป็นการดีที่ทราบว่าคุณอาจพบโฟลเดอร์ว่างในไลบรารีของคุณ ฉันใช้สคริปต์แยกต่างหากที่คุณสามารถเรียกใช้เพื่อล้างโฟลเดอร์ว่างได้ คุณสามารถค้นหาสคริปต์ฉบับเต็มได้ที่นี่บน GitHub ของฉัน
ทดสอบสคริปต์ประเภทนี้บนไซต์ทดสอบ SharePoint ด้วยไฟล์ทดสอบบางไฟล์เสมอ ตรวจสอบให้แน่ใจว่าคุณตั้งค่า whatif เป็น $true และตัวแปร บังคับ เป็น $false เมื่อคุณเริ่มต้น
หากคุณมีคำถามใด ๆ เพียงแค่แสดงความคิดเห็นด้านล่าง