Jaap Brasser’s Favorite PowerShell Tips and Tricks

When I met up with Aleksandar Nikolic at TechEd Europe I was asked if I wanted to write an article for PowerShell Magazine. I hope my tips are useful to you and feel free to leave a comment or question if you would like me to clarify anything.

 Using –ErrorVariable to quickly check effective permissions for a user

When working with permissions it is useful to know to which folders a user has access and to which folders access is denied. Although the Windows GUI does offer options for effective permissions, this is hardly appropriate when a large folder structure should be checked. This is where Get-ChildItem comes into play:

Get-ChildItem –Force –Recurse –ErrorAction SilentlyContinue –ErrorVariable AccessDenied

This will display all files and folders that are accessible, and stores all inaccessible folders in the AccessDenied array. For the purpose of this example we will focus on the files and folders that generated an error. We do this by omitting the output of Get-ChildItem by writing the output to the special variable $null:

$null = Get-ChildItem –Force –Recurse –ErrorAction SilentlyContinue –ErrorVariable AccessDenied

This gives us a list that contains all errors that have been generated by Get-ChildItem which conveniently gives us all the paths that were inaccessible.

$AccessDenied | ForEach-Object {$_.Exception}

This command displays all the inaccessible paths including the error messages. To just display paths we can use the TargetObject property.

$AccessDenied | ForEach-Object {$_.TargetObject}

This will display the names of the files and folders that were inaccessible. Because this catches all errors there might also be other reasons besides permissions which might be restricting access to a file or folder. Therefore it is important to verify the errors before assuming why access might have been denied to a folder.

As a bonus, since we should already be using PowerShell v3, we can shorten the last two commands:

$AccessDenied.Exception
$AccessDenied.TargetObject

Parse Robocopy output in PowerShell to find long path names – Workaround for 260 character limit in Windows

The specified path, file name, or both are too long. The fully qualified file name must be less than 260 characters, and the directory name must be less than 248 characters.

The dreaded error, I think everyone working with Windows has run into this at some point. Even in Windows 8/Server 2012 we are still limited by this. It might be a file server with an exotic folder structure or just a single file with a long name that we cannot get rid of. This tip will show how to use Robocopy to detect the files and folders that have a long path.

Let’s start out with a simple example displaying the difference in speed of using Get-ChildItem in combination with Measure-Object, or by using Robocopy to count the number of files and the total size of a folder:

(Measure-Command {1..10 | ForEach-Object {Get-ChildItem C:\Windows -Recurse -Force -ErrorAction SilentlyContinue | Where-Object {!$_.PSIsContainer} | Measure-Object Length -Sum}}).TotalSeconds
283.4675185

(Measure-Command {1..10 | ForEach-Object {robocopy.exe c:\windows c:\doesnotexist /l /e /b /copy:d /np /fp /nfl /ndl /njh}}).TotalSeconds
51.6469264

Both these commands generate similar output, but by using Robocopy we manage to get the results several times faster.

Now on to the more interesting stuff; using Robocopy to detect long file and folder paths. Since Robocopy uses a predictable output we can parse its output to extract the information we require. In this scenario we will use Robocopy to find files with a path longer than the maximum allowed. The next example will output a list of files that have a path longer than 260 characters:

robocopy.exe c:\deeppathtest c:\doesnotexist /l /e /b /np /fp /njh /njs /ndl | Where-Object {$_.Length -ge 286} | ForEach-Object {$_.Substring(26,$_.Length-26)}

To simplify this command I have written a short function to this process. It can be downloaded from the TechNet Script Repository: Get-LongPathName.ps1

Using this function, we can search for files and folders with long pathnames. The function outputs an object that contains the PathLength, Type (File or Folder) and the FullPath to the file or folder. Here is an example of how to utilize this function:

Get-LongPathName -FolderPath ‘C:\Deeppathtest’ -MaxDepth 200

This will output all files and folders with a path longer than 200 characters in the C:\Deeppathtest folder. And because this function returns an object the output can be sorted and otherwise manipulated making the output easy to work with.

Function Get-LongPathName

Function Get-LongPathNames {
    [CmdletBinding()]
    param(
        [Parameter(Position=0,Mandatory=$true,ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)]
        [string[]]$FolderPath,

        [ValidateRange(10,248)][int16]$MaxDepth=248
    )
    
    begin {
        if (!(Test-Path -Path $(Join-Path $env:SystemRoot 'System32\robocopy.exe'))) {
            write-warning "Robocopy not found, please install robocopy"
            return
        }

        $Results = @()
	}
	
	process {
    	foreach ($Path in $FolderPath) {
            $RoboOutput = robocopy.exe $Path c:\doesnotexist /l /e /b /np /fp /njh /njs 

            # Build Array for files. 26 is added to $MaxDepth the text string generated by Robocopy
            $RoboOutput | Select-String "New File" | Where-Object {$_.Line.Length -ge $MaxDepth+26} | ForEach-Object {
                $Props = @{
                    FullPath= $_.Line.Substring(26,$_.Line.Length-26)
                    Type = 'File'
                    PathLength = $_.Line.Length-26
                }
                $Results += New-Object -TypeName PSCustomObject -Property $Props
            } 

            # Append folders to array
            # Build Array for files
            $RoboOutput | Select-String "New Dir" | Where-Object {$_.Line.Length -ge ($MaxDepth+22)} | ForEach-Object {
                $Props = @{
                    FullPath = $_.Line.Substring(22,$_.Line.Length-22)
                    Type = 'Folder'
                    PathLength = $_.Line.Length-22
                }

                $Results += New-Object -TypeName PSCustomObject -Property $Props
            }
   		}
	}

	end {
        $Results
    }
}
Share on: