Weboldal keresés

A SharePoint duplikált mappáinak egyesítése a PowerShell-lel


A blog egyik olvasója megkeresett, ha tudom, hogyan egyesíthetem az ismétlődő mappákat a SharePoint Online-ban. A felhasználók már dolgoztak a duplikált mappákban, így nem csak egyesíteni kellett a mappákat, hanem meg kell őriznünk az utoljára módosított fájlt is.

Minden duplikált fájl vagy mappa fájlnevében ugyanaz a minta, (1). Így nagyon egyszerűvé vált a fájlok és mappák megtalálása, valamint az eredeti hellyel való egyesítése.

Ha kíváncsi, mi történt; Egy felhasználó helyileg törölte a fájlokat anélkül, hogy először leállította volna a szinkronizálást. A törölt fájlok nagy száma miatt (több mint 750 000) megkérték a Microsoftot, hogy állítsa vissza őket, amit meg is tettek.

Csak egy kommunikációs hiba miatt a visszaállítási munka két részből állt, ami duplikált fájlokat eredményezett. A Microsoft nem tudott segíteni a tisztításban, ezért közbeléptem.

Ez azon helyzetek egyike, amikor egy harmadik féltől származó biztonsági mentési megoldás valóban életmentő lehet. Korábban írtam róla; Szüksége van-e biztonsági mentési megoldásra a Microsoft 365-höz. Mint látható ebben az esetben, a fájlok visszaállíthatók, de a biztonsági mentési megoldás sokkal egyszerűbbé tenné a visszaállítást.

Az ismétlődő elemek megkeresése

Az ismétlődő mappák és fájlok egyesítése néhány lépést vesz igénybe. A duplikált mappák nem csak a dokumentumtár gyökér szintjén találhatók, hanem egy almappa 3 szint mélységében is lehetnek. Tehát rekurzívan át kell dolgoznunk az összes mappán, és meg kell keresnünk azokat az elemeket, amelyek nevében (1) szerepel.

A szkriptet néhány lépésre bontottam, mindegyik saját funkcióra fordítva:

  • Ismétlődő fájlok és mappák keresése
  • A cél útvonal létrehozása (az eredeti hely)
  • A mappa tartalmának áthelyezése
  • Hasonlítsa össze a fájlok dátumait
  • Egyetlen elem mozgatása

Csatlakozzon a SharePoint webhelyhez

Mielőtt bármit is tehetünk, csatlakoznunk kell a SharePoint webhelyhez. Ezt egyszerűen megtehetjük a PnPOnline-nal, én pedig a webes bejelentkezési kapcsolót használom, így itt használhatjuk a normál bejelentkezésünket az MFA-val.

A cikk végén megmutatom a teljes forgatókönyvet.

# SharePoint url
$sharepointUrl = 'https://lazyadmin.sharepoint.com/'

# Site
$site = 'sites/lab01'

# Library name
$libraryName = 'Duplicates'

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

Az ismétlődő fájlok megkeresése

Mivel az almappákat is fel kell dolgoznunk, itt az a trükk, hogy rekurzívan menjünk végig a mappákon. Itt a Get-PnPFolderItem függvényt használjuk, de akkor rekurzív módon. A funkció Josh Einstein forgatókönyvén alapul, amelyet itt találhat meg.

# 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
        }
    }
}

Tehát minden elemnél ellenőrizzük, hogy mappa-e. Ha ez egy mappa, akkor ellenőrizzük a mappa nevét, ha az (1) tartalmazza. Ha igen, akkor áthelyezzük a mappa tartalmát, ha nem, akkor „nyitjuk” a mappát, és végignézzük a tartalmat.

Ha nem mappa, hanem fájl, akkor ismét ellenőrizzük a nevet. Ha az (1) értéket tartalmazza, akkor áthelyezzük a fájlt, különben nem csinálunk semmit.

A cél útvonal létrehozása

Ha ismétlődő elemet találunk, létre kell hoznunk az eredeti elérési utat az elem elérési útjából. Azt is ellenőriznünk kell, hogy létezik-e az eredeti mappa, és ha nem, akkor létre kell hozni újra az eredeti mappát.

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)', '')
    }
}

Amit az egyes függvényekben látni fog, az az, hogy a $whatIf változót használom. A szkript elején deklaráltam ezt a változót. Ez lehetővé teszi a szkript tesztelését anélkül, hogy ténylegesen áthelyeznék vagy létrehoznék fájlokat vagy mappákat.

A legtöbb PnP funkció támogatja a WhatIf kapcsolót, de például a Resolve-PnPFolder nem támogatja azt. Így egyszerűen tesztelhetem úgy, hogy felírom a konzolra, hogy mit csinálna a szkript.

A create-targetPath funkció újra létrehozza az elérési utat, ellenőrzi, hogy létezik-e a mappa, és ha nem, akkor újra létrehozza a Resolve-PnPfolder funkcióval.

Mappa és almappák áthelyezése a PowerShell segítségével

Ha ismétlődő mappát találtunk, a mappát és az összes tartalmat (beleértve az almappákat is) az eredeti helyére szeretnénk helyezni. Minden almappához létre kell hoznunk egy cél elérési utat is.

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
        }
    }
}

A fájlok dátumának összehasonlítása a SharePoint Online-ban és a fájlok áthelyezése

Tehát most elérkeztünk ahhoz a részhez, amelyről szó van, a tényleges fájlok áthelyezésére. Mielőtt áthelyezhetnénk a fájlt, ellenőriznünk kell, hogy a fájl létezik-e az eredeti helyen. Ha igen, akkor össze kell hasonlítanunk a fájldátumokat, ebben az esetben az utoljára módosított fájlt szeretnénk megtartani.

# 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"
            }
        }
    }    
}

Itt hozzáadtam egy számlálót, amely számolja az áthelyezett fájlok számát. Amikor egy ilyen szkriptet tesztel, először csak néhány fájlt szeretne áthelyezni, majd ellenőrizni az eredményeket, mielőtt folytatná.

# 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
    }
}

Tehát összehasonlítjuk a dátumokat, és ha az eredeti fájl újabb, akkor eltávolítjuk a duplikált fájlt. A fájl eltávolítása funkcióban hozzáadtam a -Recycle kapcsolót, így a fájl a lomtárba kerül. Eltávolíthatja ezt a kapcsolót, ha véglegesen törölni szeretné a fájlokat.

Becsomagolás

Jó tudni, hogy előfordulhat, hogy néhány üres mappa marad a könyvtárában. Külön szkriptet használtam, amelyet futtathat az üres mappák megtisztításához. A teljes szkriptet itt találod a GitHubon.

Mindig tesztelje az ilyen típusú szkripteket egy teszt SharePoint webhelyen néhány tesztfájllal. Győződjön meg róla, hogy a whatif értéket $true értékre, a force változót pedig $false értékre állítja az induláskor.

Ha bármilyen kérdése van, csak írjon megjegyzést alább.

Kapcsolódó cikkek