Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PowerShell copy fails without warning

Howdy, am trying to copy a file from IE cache to somewhere else. This works on w7, but not Vista Ultimate.

In short:

copy-item $f -Destination "$targetDir" -force

(I also tried $f.fullname)

The full script:

$targetDir = "C:\temp"
$ieCache=(get-itemproperty "hkcu:\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders").cache

$minSize = 5mb
Write-Host "minSize:" $minSize
Add-Content -Encoding Unicode -Path $targetDir"\log.txt" -Value (get-Date)

Set-Location $ieCache
#\Low\Content.IE5 for protected mode
#\content.ie5 for unprotected

$a = Get-Location 

foreach ($f in 
        (get-childitem -Recurse -Force -Exclude *.dat, *.tmp | where {$_.length -gt $minSize})
        )
        {           
        Write-Host (get-Date)   $f.Name $f.length       
        Add-Content -Encoding Unicode -Path $targetDir"\log.txt" -Value $f.name, $f.length
        copy-item $f -Destination "$targetDir" -force
        }

End of wisdom. Please help!

like image 457
boink Avatar asked Mar 20 '10 11:03

boink


2 Answers

Anytime you're having problems trying to figure out why a parameter doesn't bind correctly in PowerShell, use Trace-Command like so:

Trace-Command -Name ParameterBinding -expr { copy-item $f foo.cat.bak } -PSHost

In this case, it works for me. Perhaps this is a "feature" of PowerShell 2.0 but you can see it attempt to bind several different times before it hits on:

COERCE arg to [System.String]
    Trying to convert argument value from System.Management.Automation.PSObject to System.String
    CONVERT arg type to param type using LanguagePrimitives.ConvertTo
    CONVERT SUCCESSFUL using LanguagePrimitives.ConvertTo: [C:\Users\Keith\foo.cat]

The way this normally works when the FileInfo objects are passed via the pipeline, they bind by "PropertyName" to the LiteralPath parameter. I know, you're probably wondering, uh didn't think System.IO.FileInfo had a LiteralPath property. Heheh, it doesn't. Those tricky PowerShell folks snuck a PSPath alias onto the LiteralPath parameter and PowerShell "adapts" each the FileInfo object to add a number of PS* properties including PSPath. So if you wanted to "literally" match the pipelining behavior you would use:

Copy-Item -LiteralPath $f.PSPath $targetDir -force

Note that you don't have to quote $targetDir in this case (as a parameter argument).

like image 57
Keith Hill Avatar answered Nov 15 '22 05:11

Keith Hill


The reason why copy-item doesn't work is that you pass System.IO.FileInfo as parameter -path. There are two possibilities how to do it correctly:

  1. copy-item -literal $f.Fullname -destination ...
  2. $f | copy-item -destination ...

Note that I use parameter -literalPath because files in the temp folder have usually [ and ] inside the name which acts as wildcards characters.

If you wonder why case #2 works, have a look at 'help Copy-Item -Parameter Path`, you will see Accept pipeline input? true (ByValue, ByPropertyName). For more info what it means look at Keith's ebook Effective Windows PowerShell.

Why you version doesn't work? Because paramerer -path (which is at position 1) takes input of type [string[]] and not FileInfo. So PowerShell tried to convert it to [string] (and then to the array) but it probably used only Name property. You can try it like this:

[string[]] (gci | select -first 1)

like image 22
stej Avatar answered Nov 15 '22 05:11

stej