3

I've created a proxy function for Remove-Item, which deletes to the recycle bin instead of permanently (using the proxy so that I can seamlessly replace the rm alias, without breaking 3rd party scripts).

However, it doesn't work when a file is piped into the function. The heart of the proxy function is this:

if ($PSBoundParameters['DeletePermanently'] -or $PSBoundParameters['LiteralPath'] -or $PSBoundParameters['Filter'] -or $PSBoundParameters['Include'] -or $PSBoundParameters['Exclude'] -or $PSBoundParameters['Recurse'] -or $PSBoundParameters['Force'] -or $PSBoundParameters['Credential']) {
    if ($PSBoundParameters['DeletePermanently']) { $PSBoundParameters.Remove('DeletePermanently') | Out-Null }
    $scriptCmd = {& $wrappedCmd @PSBoundParameters }
} else {
    $scriptCmd = {& Recycle-Item -Path $PSBoundParameters['Path'] }
}

So, my custom Recycle-Item function is only called if Path is the only parameter. So, something like Get-ChildItem .\temp\ | rm -DeletePermanently works just fine, but Get-ChildItem .\temp\ | rm has an error because the Path passed to Recycle-Item is $null.

I've tried passing $Path instead of $PSBoundParameters['Path'] and tried splatting @PSBoundParameters like the call to $wrappedCmd above, but none of it appears to do much good. I've copied the params from this function to Recycle-Item, to ensure that it is expecting input from the pipeline, but that doesn't seem to help either. Some of those changes appear to pass along the file name, but not the full path, so I don't know if there's some magic inside Remove-Item that I need to replicate to handle a file object from the pipeline.

Recycle-Item is just a basic function:

function Recycle-Item($Path) {
    $item = Get-Item $Path
    $directoryPath = Split-Path $item -Parent

    $shell = new-object -comobject "Shell.Application"
    $shellFolder = $shell.Namespace($directoryPath)
    $shellItem = $shellFolder.ParseName($item.Name)
    $shellItem.InvokeVerb("delete")
}
1
  • 2
    Piping Get-ChildItem to Remove-Item will bind on LiteralPath, not Path Commented Apr 17, 2017 at 17:16

1 Answer 1

2

As mentioned in the comments, the provider cmdlets usually bind on LiteralPath when you pipe objects between them. This way allows Path to support wildcard globbing without the chance of passing ambiguous item paths between cmdlets.

Remove-Item has only two parameter sets, and they are named after their mandatory parameters, Path and LiteralPath

To solve your problem, simply check for all defined parameters that are not one of these two, then pass the appropriate value to Remove-Item based on the $PSCmdlet.ParameterSetName value:

if(@($PSBoundParameters.Keys |Where-Object {@('DeletePermanently','Filter','Include','Exclude','Recurse','Force','Credential') -contains $_}).Count -ge 1){
    # a parameter other than the Path/LiteralPath or the common parameters was specified, default to Remove-Item
    if ($PSBoundParameters['DeletePermanently']) { 
        $PSBoundParameters.Remove('DeletePermanently') | Out-Null 
    }
    $scriptCmd = {& $wrappedCmd @PSBoundParameters }
} else {
    # apart from common parameters, only Path/LiteralPath was specified, go for Recycle-Item
    $scriptCmd = {& Recycle-Item -Path $PSBoundParameters[$PSCmdlet.ParameterSetName] }
}
Sign up to request clarification or add additional context in comments.

1 Comment

Even when piping from Get-ChildItem, PsCmdlet.ParametersSetName is Path (and the check for $PSBoundParameters['LiteralPath'] is $false), so I don't think that's the case here (or there's a bigger issue…)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.