Archive for September, 2006

Watch-Command.ps1 – watch commands output repeatedly


Synopsis

The script executes a command repeatedly, collects the output and displays exactly one screen portion of it; out-of-screen lines and line ends are truncated. The script avoids screen blinking effect because normally it does not use Clear-Host, instead it refills the entire screen with fresh output. This is the good. The bad is that this approach may not work if a command itself operates on console output directly (e.g. uses *-Host).


Examples

# PowerShell commands:
Watch-Command {Get-Process}
Watch-Command {Get-Process | Format-Table Name, Id, WS, CPU, Description, Path -Auto}

# Native executable commands:
Watch-Command {netstat -e -s}
Watch-Command {cmd /c dir /a-d /o-d %temp%}

Watch-Command.ps1

##
## Author   : Roman Kuzmin
## Synopsis : Watch one screen of commands output repeatedly
## Modified : 2006.11.06
##
## -Command: script block which output is being watched
## -Seconds: refresh rate in seconds
##
## *) Commands should not operate on console.
## *) Tabs are simply replaced with spaces.
## *) * indicates truncated lines.
## *) Empty lines are removed.
## *) Ctrl-C to stop.
##

param([scriptblock]$Command_ = {Get-Process}, [int]$Seconds_ = 3)

$private:sb = New-Object System.Text.StringBuilder
$private:w0 = $private:h0 = 0
for(;;) {

    # invoke command, format output data
    $private:n = $sb.Length = 0
    $private:w = $Host.UI.RawUI.BufferSize.Width
    $private:h = $Host.UI.RawUI.WindowSize.Height-1
    [void]$sb.EnsureCapacity($w*$h)
    .{
        & $Command_ | Out-String -Stream | .{process{
            if ($_ -and ++$n -le $h) {
                $_ = $_.Replace("`t", ' ')
                if ($_.Length -gt $w) {
                    [void]$sb.Append($_.Substring(0, $w-1) + '*')
                }
                else {
                    [void]$sb.Append($_.PadRight($w))
                }
            }
        }}
    }>$null

    # fill screen
    if ($w0 -ne $w -or $h0 -ne $h) {
        $w0 = $w; $h0 = $h
        Clear-Host; $private:origin = $Host.UI.RawUI.CursorPosition
    }
    else {
        $Host.UI.RawUI.CursorPosition = $origin
    }
    Write-Host $sb -NoNewLine
    $private:cursor = $Host.UI.RawUI.CursorPosition
    if ($n -lt $h) {
        Write-Host (' '*($w*($h-$n)+1)) -NoNewLine
    }
    elseif($n -gt $h) {
        Write-Host '*' -NoNewLine
    }
    $Host.UI.RawUI.CursorPosition = $cursor
    Start-Sleep $Seconds_
}

4 Comments

Select a random object from pipeline effectively


Problem

Select a random object from the input pipeline without knowing the number of objects and without collecting the entire input.

Solution

There is a beautiful and perfectly simple algorithm. The example in Perl and details are here: "Picking a Random Line from a File".


PowerShell script

##
## Author   : Roman Kuzmin
## Synopsis : Select a random object from input pipeline
## Modified : 2006.09.21
##
## Returns a selected random object
## -Seed: seed for System.Random; negative ~ time based; default: -1
##

param([int]$Seed = -1)

begin
{
    $random = $(if ($Seed -ge 0) {New-Object Random $Seed} else {New-Object Random})
    $object = $null
    $number = 0
}
process
{
    if ($random.Next(++$number) -eq 0) {
        $object = $_
    }
}
end
{
    $object
}

Leave a comment

Measure-CommandEx.ps1 – Runs Measure-Command several times

UPDATE: A newer version is available: https://gist.github.com/1272334


Synopsis

This script executes an expression specified number of times and returns its average duration as [System.TimeSpan]. It displays progress information and difference between two last average duration values. For performance reason progress is updated every second, not every iteration. If delta becomes relatively stable and small enough execution may be stopped by pressing escape: in this case the returned average duration is calculated for the current number of iterations.

Note that in contrast to cmdlet Measure-Command: *) expressions are executed in the inner scope; *) pipelined input is not supported.


Measure-CommandEx.ps1

##
## Author   : Roman Kuzmin
## Synopsis : Measure-Command with iteration count and progress
## Modified : 2006.11.06
##
## Returns averaged duration of -Expression as [timespan]
## -Expression: the expression being executed and timed
## -Count: execute -Count iterations of -Expression
## -NoProgress: don't show progress information
## -NoEscape: don't return on escape
##

param
(
    [scriptblock]$Expression_ = {},
    [int]$Count_ = 10000,
    [switch]$NoProgress_,
    [switch]$NoEscape_
)

# title
$private:title = "Measuring: {$Expression_}"
if (!$NoEscape_) {
    $title = '[Escape] to return. ' + $title
}
if ($NoProgress_) {
    Write-Host $title
}

# iterations
[int64]$private:ticks = 0
$private:time = [datetime]::Now.Date
for($private:e = 1; $e -le $Count_; ++$e) {

    # update the sum
    $private:ticks0 = $ticks
    $ticks += &{(Measure-Command $Expression_).Ticks}

    # update progress each second
    if (!$NoProgress_ -and ([datetime]::Now - $time).TotalSeconds -gt 1) {
        $time = [datetime]::Now
        $private:diff = [int]([math]::Abs($ticks - $ticks0)/$ticks*10000)/100
        Write-Progress $title "Average delta: $diff%" -PercentComplete (100*$e/$Count_)
    }

    # check for escape
    if (!$NoEscape_ -and $Host.UI.RawUI.KeyAvailable) {
        $private:key = $Host.UI.RawUI.ReadKey(14)
        if ($key.KeyDown -and $key.VirtualKeyCode -eq 27) {
            $Count_ = $e
            break
        }
    }
}

# the result
[timespan][int64]($ticks/$Count_)

Leave a comment