Sonntag, 31. Januar 2010

Out-ISEFile (V 0.9)

Just read the following tweet:

@MaxTrinidad Need the path to the various profile scripts on a system? Try $profile.psextended | Format-List (Try it in #PowerShell ISE too)

from @alexandair.

As I'm using ISE all day long, I tried it immedeatly and here is my result:
AllUsersAllHosts : C:\Windows\System32\WindowsPowerShell\v1.0\profile.ps1
AllUsersCurrentHost : C:\Windows\System32\WindowsPowerShell\v1.0\Microsoft.PowerShellISE_pro
file.ps1
CurrentUserAllHosts : C:\Users\berndk.MMEDVNT\Documents\WindowsPowerShell\profile.ps1
CurrentUserCurrentHost : C:\Users\berndk.MMEDVNT\Documents\WindowsPowerShell\Microsoft.PowerShe
llISE_profile.ps1



Oops again the PowerShells f***ing wrapping behavior. It's time to write a work around:

function Out-IseFile            
{

[CmdletBinding()]
param (
[Parameter(Position = 0, Mandatory = $True, ValueFromPipeline = $True)]
$msg,
[Parameter(Position = 1, Mandatory = $False, ValueFromPipeline = $False)]
$path = $null,
[switch]$fl
)
if ($fl)
{
$msg = $msg | Format-list | out-string -width 2000
}

$count = $psise.CurrentPowerShellTab.Files.count
$null = $psIse.CurrentPowerShellTab.Files.Add()
$Newfile = $psIse.CurrentPowerShellTab.Files[$count]
if ($path)
{
$NewFile.SaveAs($path)
$NewFile.Save([Text.Encoding]::default)
}
$Editor = $Newfile.Editor
#$Editor.SetCaretPosition($Editor.LineCount, 1)

$ar = $msg -split "`r`n" | %{ $_ -replace '\s*$', '' }

$msg = $ar -join "`r`n"

$msg = $msg + "`r`n"
$Editor.InsertText($msg)
$Newfile

}


$null = $profile.psextended | out-IseFile -fl



To use it just type:
$null = $profile.psextended | out-IseFile -fl


Please note: this is work in progress.
The main trick is to use Out-String with a long width parameter, that suppresses the unwanted wrapping.
For some reason (I guess it's just what we call in German Gedankenlosigkeit) Out-String emits trailing blanks. I included code to get rid of it.

Because Format-List seams rather frequent, I added the fl switch.

Remeber when ISE-Output pane behaves naughty ( and that is quite often) just send your output to a fresh editor. That makes it easy to edit and save it, if you want.

Have fun extending ISE.

Bernd

PS: Thanks to JS who made it extendable.

Sonntag, 24. Januar 2010

Remove-DuplicateFile the ISE Way

The last days I spent checking some PowerShell scripts for finding and deleting duplicate files.

First we need to define which files are equal.
For most big files the criteria is simple: they are equal, when they are true copies of each other.
The second group contains text files you have edited. Here you often want to ignore some insignificant differences like trailing blanks etc. Take a look at the options of the old fc command

Let us focus today on the first approach. The best solution I found on the web is from Jason Stangroome. From him I copied the idea to use the Get-MD5 function, which closes the streams.

Without it you have some fun googling for 'The Process cannot access the file because a it is being used by another process'. Yes there seems to be ways to close files even in PowerShell, but PowerShell documentation is somewhat biased.

Using those Get-MD5 function, there is no problem in invoking Remove-item on the File you found, if you are decided to delete it.

I would prefer to use the delete method of the [System.IO.FileInfo], but I get 'Exception calling "Delete" with "0" argument(s): "Access to the path 'xxx.ps1' is denied.'
On the other side
Remove-item $($file.Fullname) -force

works fine.

I'm not following Jason in sorting the files acording to there length, but keep to a simpler design of comparing the files in the order they are output by Get-Childitem.

Instead of using -whatif I chose a different way. I just output the commands and some comments to a new ISE-file.

There I can check, whether I want to perform the delete or not and at the same time I have a fine log of the files I deleted.

The following code assumes you use PowerShell V2' ISE:

function Get-MD5([System.IO.FileInfo] $file = $(throw 'Usage: Get-MD5 [System.IO.FileInfo]'))            
{
# This Get-MD5 function sourced from:
# http://blogs.msdn.com/powershell/archive/2006/04/25/583225.aspx
$stream = $null;
$cryptoServiceProvider = [System.Security.Cryptography.MD5CryptoServiceProvider];
$hashAlgorithm = new-object $cryptoServiceProvider
$stream = $file.OpenRead();
$hashByteArray = $hashAlgorithm.ComputeHash($stream);
$stream.Close();

## We have to be sure that we close the file stream if any exceptions are thrown.
trap
{
if ($stream -ne $null) { $stream.Close(); }
break;
}

return [string]$hashByteArray;
}

function New-IseFile ($path)
{
$count = $psise.CurrentPowerShellTab.Files.count
$null = $psIse.CurrentPowerShellTab.Files.Add()
$Newfile = $psIse.CurrentPowerShellTab.Files[$count]
$NewFile.SaveAs($path)
$NewFile.Save([Text.Encoding]::default)
$Newfile

}

function Write-IseFile($file, $msg)
{
$Editor = $file.Editor
$Editor.SetCaretPosition($Editor.LineCount, 1)
$Editor.InsertText(($msg + "`r`n"))
}


function Remove-DuplicateFile
{
[CmdletBinding()]
param (
$path = ”.\”,
$extension = ”*.*”,
[switch]$delete
)


$Newfile = New-IseFile "$(get-location)\Delete-DuplicateFiles_$(get-date -f "yyyy-MM-dd-HH").ps1"

$hashtable = new-object system.collections.hashtable

# $global:filesToDelete = @()
$global:totalLength = 0

get-childitem -path $path $extension -recurse | where-object { ! $_.PSIsContainer } |
% {
$file = $_
$hashvalue = Get-MD5 $file
$length = $_.Length
trap {
$global:totalLength += $length
$msg = @"

# current {0} Byte total: {1,8:f3} MB"
# Remove-Item `'$($hashtable[$hashvalue])`' -force
Remove-Item `'$($file.Fullname)`' -force
"@
-f $length, $($global:totalLength/ 1MB)
$msg
Write-IseFile $Newfile $msg
# $global:filesToDelete += $file.Fullname
if ($delete)
{
Remove-item $($file.Fullname) -force
#$file.delete()

}
continue
}
$hashTable.Add($hashvalue, $file.FullName)
}

# Write-Host "`r`nFiles to delete`r`n"

# foreach ($f in $filesToDelete) {
# Write-Host "Remove-Item `'$f`' -force"
# }
$NewFile.Save()
}



Now you I call it with something like

Remove-DuplicateFile D:\myFiles,D:\mybackup

and get the new new ISE-Editor.

Attention using the -delete parameter the deletes where executed immediately.

Sonntag, 17. Januar 2010

My Diskqueue Problem

I'm running Windows 7 and every now and than my system is nonresponsive for 60 seconds.
The esource monitor shows something like the following. Most of my current PowerShell scripts are attemps to trace down this problem.



Any ideas ?

Eval-Min, Eval-Max, $null Part II

# The following relations are valid            
# All PowerShellStatements eval to True

# -2 < -1 < NULL < 0 < 1 < 2

-2 -lt -1
-1 -lt $null
$null -lt 0
0 -lt 1
1 -lt 2

$null -lt 1

# 2 > 1 > 0 > NULL > -1 > -2

2 -gt 1
1 -gt 0
0 -gt $null
$null -gt -1
-1 -gt -2

1 -gt $null


# 0 is not NULL

0 -ne $null
$null -ne 0

function Eval-Max
{
$max = $null
foreach ($i in $args){
if ($i -ne $null){
if( $i -gt $max -or $max -eq $null){
$max = $i
}
}
}
$max
}

function Eval-Min
{
$min = $null
foreach ($i in $args){
if ($i -ne $null){
if( $i -lt $min -or $min -eq $null){
$min = $i
}
}
}
$min
}

# $null handeled korrect

Eval-Min $null -1
Eval-Max $null -1
Eval-Min -1 $null
Eval-Max -1 $null


# The following seem wrong

Eval-Min -2 -1
Eval-Max -2 -1


To understand what happens look at

function Show-Type (){             
($args[0]).gettype()
}
Show-type 1
Show-type -1

IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Int32 System.ValueType
True True String System.Object



I find this different behaviour is rather confusing. May we call it a bug?

When you cast the arguments to integers, you get the expected result.

Eval-Min $([int]-2) $([int]-1)            

-2


When you are working with properly typed input, the functions work as aspected.

Samstag, 16. Januar 2010

Eval-Min, Eval-Max, $null

When you want to determine the minimum or maximum of some property of all items in a pipeline, you can use Measure-Object.

But when you want to do some kind of performance monitoring, you want to eval the min or max so far, without stoping the running pipeline.

Primary to improve readability in my script I wrote the following two functions.

Attention I just found wrong results for negative arguments. I'm still checking. Look at the follow-ups.

function Eval-Max             
{
$max = $null
foreach ($i in $args){
if ($i -gt $max){
$max = $i
}
}
$max
}


function Eval-Min
{
$min = $null
foreach ($i in $args){
if (($i -ne $null -and $i -lt $min) -or $min -eq $null){
$min = $i
}
}
$min
}


Note that the two functions are not symmetric. Unlike SQL here in PowerShell we have

$null -lt 1            
True

$null -gt 1
False


PowerShell $null seems to have funny mathematical behaviour. It is less than 0 but greater than each negative number. In any case it is different from SQL NULL. be warned. Me functions here follow the SQL pattern. Therefore

Eval-Min $null 1            
1


Please if you know of earlier similar PowerShell functions drop a comment.

Montag, 4. Januar 2010

Use PowerShell to get openfiles with handle.exe

You find handle.exe at http://technet.microsoft.com/en-us/sysinternals/bb896655.aspx.

You can use the following script to store a snapshot of the open files in a grid. You have to adapt the path to handle.exe.

C:\Usr\sysint\handle.exe | foreach{            
if ($_ -match '^(?<program>\S*)\s*pid: (?<pid>\d*)\s*(?<user>.*)$') {
$matches | %{
$id = $_.pid
$program = $_.program
$user = $_.user
}
}
if ($_ -match '^\s*(?<handle>[\da-z]*): File \((?<attr>...)\)\s*(?<file>(\\\\)|([a-z]:).*)') {
#$_
$matches | select @{n="Pid";e={$id}}, @{n="Program";e={$program}}, @{n="User";e={$user}}, @{n="Handle";e={$_.handle}}, @{n="attr";e={$_.attr}}, @{n="Path";e={$_.file}}
}

} | out-Gridview



I would like to fetch just the files in the current disk queue.

2010-01-16 Edited. I had a problem with html escape characters. Thanks for the comment.