Last active
April 1, 2019 18:52
-
-
Save mklement0/79bfa6697244ca3d432f4e85a10e5376 to your computer and use it in GitHub Desktop.
Get-OpenFiles: PowerShell function for locating open files in a given directory [subtree]
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function Get-OpenFiles { | |
<# | |
.SYNOPSIS | |
Finds open files. | |
.DESCRIPTION | |
Finds files currently being held open by any process and reports them as | |
[System.IO.FileInfo] instances, as Get-ChildItem would. | |
In fact, this function supports all parameters that Get-ChildItem does and | |
uses the latter behind the scenes, while filtering the output to Include | |
only in-use files. | |
Files that cannot be accessed due to lack of permissions cause | |
non-terminating errors. | |
.EXAMPLE | |
Get-OpenFiles | |
Lists all open files in the current directory, if any. | |
.EXAMPLE | |
Get-OpenFiles *.txt | |
Lists open files among those with extension *.txt in the current directory. | |
.Example | |
Get-OpenFiles $HOME/Documents -Recurse | |
Lists open files located anywhere in the user's Documents folder and its | |
entire subtree. | |
Note that running with -Recurse can take a while. | |
.Example | |
[bool] (Get-OpenFiles ./someFile.txt) | |
Indicates if the specified file is open or not. | |
#> | |
[CmdletBinding(DefaultParameterSetName = 'Items', PositionalBinding = $False)] | |
param( | |
[Parameter(ParameterSetName = 'Items', Position = 0, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] | |
[string[]] | |
${Path}, | |
[Parameter(ParameterSetName = 'LiteralItems', Mandatory = $true, ValueFromPipelineByPropertyName = $true)] | |
[Alias('PSPath', 'LP')] | |
[string[]] | |
${LiteralPath}, | |
[Parameter(Position = 1)] | |
[string] | |
${Filter}, | |
[string[]] | |
${Include}, | |
[string[]] | |
${Exclude}, | |
[Alias('s')] | |
[switch] | |
${Recurse}, | |
[uint32] | |
${Depth}, | |
[switch] | |
${Force}, | |
[switch] | |
${Name}) | |
dynamicparam { | |
try { | |
$targetCmd = $ExecutionContext.InvokeCommand.GetCommand('Microsoft.PowerShell.Management\Get-ChildItem', [System.Management.Automation.CommandTypes]::Cmdlet, $PSBoundParameters) | |
$dynamicParams = @($targetCmd.Parameters.GetEnumerator() | Microsoft.PowerShell.Core\Where-Object { $_.Value.IsDynamic }) | |
if ($dynamicParams.Length -gt 0) { | |
$paramDictionary = [Management.Automation.RuntimeDefinedParameterDictionary]::new() | |
foreach ($param in $dynamicParams) { | |
$param = $param.Value | |
if (-not $MyInvocation.MyCommand.Parameters.ContainsKey($param.Name)) { | |
$dynParam = [Management.Automation.RuntimeDefinedParameter]::new($param.Name, $param.ParameterType, $param.Attributes) | |
$paramDictionary.Add($param.Name, $dynParam) | |
} | |
} | |
return $paramDictionary | |
} | |
} | |
catch { | |
throw | |
} | |
} | |
begin { | |
try { | |
$outBuffer = $null | |
if ($PSBoundParameters.TryGetValue('OutBuffer', [ref]$outBuffer)) { | |
$PSBoundParameters['OutBuffer'] = 1 | |
} | |
$wrappedCmd = $ExecutionContext.InvokeCommand.GetCommand('Microsoft.PowerShell.Management\Get-ChildItem', [System.Management.Automation.CommandTypes]::Cmdlet) | |
$scriptCmd = { | |
& $wrappedCmd @PSBoundParameters | Where-Object { | |
if ($_.PSIsContainer) { return $false } # ignore directories | |
$file = $_ | |
try { | |
[IO.File]::Open($file.FullName, 'Open', 'Read', 'None').Close() | |
return $false # file NOT open elsewhere | |
} | |
catch { | |
# See if the exception is a sharing / locking error, which indicates that the file is open. | |
if ( | |
$_.Exception.InnerException -is [System.IO.IOException] -and | |
($_.Exception.InnerException.HResult -band 0x21) -in 0x21, 0x20 | |
) { | |
return $true # file IS open elsewhere | |
} | |
else { | |
# Unexpected error, relay the exception as a non-terminating error. | |
$PSCmdlet.WriteError([System.Management.Automation.ErrorRecord]::new( | |
$_.Exception.InnerException, # the exception to wrap | |
$null, # a custom error ID (string) | |
[System.Management.Automation.ErrorCategory]::InvalidOperation, | |
$file # the target object (what object the error relates to) | |
)) | |
} | |
} | |
} | |
} | |
$steppablePipeline = $scriptCmd.GetSteppablePipeline($myInvocation.CommandOrigin) | |
$steppablePipeline.Begin($PSCmdlet) | |
} | |
catch { | |
throw | |
} | |
} | |
process { | |
try { | |
$steppablePipeline.Process($_) | |
} | |
catch { | |
throw | |
} | |
} | |
end { | |
try { | |
$steppablePipeline.End() | |
} | |
catch { | |
throw | |
} | |
} | |
} | |
# If this script is invoked directly - as opposed to being dot-sourced in order | |
# to define the embedded function for later use - invoke the embedded function, | |
# relaying any arguments passed. | |
if (-not ($MyInvocation.InvocationName -eq '.' -or $MyInvocation.Line -eq '')) { | |
Get-OpenFiles @Args | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment