Wednesday, February 14, 2007

PowerShell, write-verbose word wraps

I think this is an error: Write-Verbose word wraps the output based on the window width.

Try to execute the last statement with different window widths (just drag the right border of the window) to see what I mean -

$VerbosePreference="continue"
write-verbose $($s="";for($i=1;$i -lt 10;$i+=1) {$s+=([string] $i * 9) + " "};$s*5)



Well, I could live with that if that was just the case interactivily, but no -
nope - nix - it also applies to a scheduled task - and which window is it then
taking the width from? Needless to say, having word-wrapped you verbose output makes it very hard to read.

Looking for word-arounds, I first tried this -

$host.ui.rawui.WindowSize.width=10000


but that assignment was just ignored. Then I tried this approach -

$current=$host.ui.rawui.WindowSize
$current.width=10000
$host.ui.rawui.WindowSize=$current


and got -

Exception setting "WindowSize": "Window cannot be wider than the screen buffer.
Parameter name: value.Width
Actual value was 10000."
At line:1 char:16
+ $host.ui.rawui.W <<<< indowsize="$current"


Well, the obvious approach is then to -

$current=$host.ui.rawui.BufferSize
$current.width=10000
$host.ui.rawui.BufferSize=$current
$current=$host.ui.rawui.WindowSize
$current.width=10000
$host.ui.rawui.WindowSize=$current


but no -


Exception setting "WindowSize": "Window cannot be wider than 200.
Parameter name: value.Width
Actual value was 10000."
At line:1 char:16
+ $host.ui.rawui.W <<<< indowsize="$current"


It seems to be limited by the values in $host.ui.rawui.MaxWindowSize or
$host.ui.rawui.MaxPhysicalWindowSize (BTW: to see the value of these, pipe
them into format-list). Then I realized, that the windows size is controlled
by the font and made some tests with that. So maybe, I could just create some
customized settings and in this way improve the situation? First, I figured out
the actual window title by logging $host.ui.rawui.windowTitle. Next, I started
my own console with that title -

cmd /c start "the wanted title" powershell


adjusted the window properties and that worked.

Next, I started to look at a scripted approach. First, I did not want to have to do this manually on some server and second, hey - this is scripting, right? - no such thing as a manual procedure please!

The scripted work-around (to cut a long story short)

When a scheduled task is running, an invinsible command line window is created.
And the settings come from the default window settings in HKCU:\Console. So could
I influence the settings but creating a subkey with the necessary properties.
This is quite simple to do from PowerShell, but I ran into another problem.
Set-ItemProperty only seems to support write string properties (e.g. registry
values), so I had to make a small dword-function as well.
Finally, I had to cope with the different titles as the values vary from XP/2003 to Vista.

Here is the resulting example -

function Set-RegistryValue($Key,$Name,$Value,$type=[Microsoft.win32.registryvaluekind]::DWord) {
$parent=split-path $key -parent
$parent=get-item $parent
$key=get-item $key
$keyh=$parent.opensubkey($key.name.split("\")[-1],$true)
$keyh.setvalue($name,$value,$type)
$keyh.close()
}
function Set-OutputBuffer($width=10000) {
$key=""
if ($host.ui.rawui.WindowTitle -eq "taskeng.exe") {
$key="hkcu:\console\taskeng.exe"
}
elseif ($host.ui.rawui.WindowTitle -eq "$($env:windir)\system32\svchost.exe" ) {
$key="hkcu:\console\%SystemRoot%_system32_svchost.exe"
}
# other titles are ignored
if ($key) {
$taskeng=$key
if (!(test-path $taskeng)) {md $taskeng -verbose}
set-RegistryValue $taskeng FontSize 0x00050000
set-RegistryValue $taskeng ScreenBufferSize 0x02000200
set-RegistryValue $taskeng WindowSize 0x00200200
set-RegistryValue $taskeng FontFamily 0x00000036
set-RegistryValue $taskeng FontWeight 0x00000190
set-ItemProperty $taskeng FaceName "Lucida Console"

$bufferSize=$host.ui.rawui.bufferSize
$bufferSize.width=$width
$host.ui.rawui.BufferSize=$BufferSize
$maxSize=$host.ui.rawui.MaxWindowSize
$windowSize=$host.ui.rawui.WindowSize
$windowSize.width=$maxSize.width
$host.ui.rawui.WindowSize=$windowSize
}

}


$verbosepreference="continue"
Set-OutputBUffer

# test code

$host.ui.rawui
write-verbose "$(get-date)"
write-verbose $($s="";for($i=1;$i -lt 10;$i+=1) {$s+=([string] $i * 9) + " "};$s*5)



The first time a scripts runs - for that user account - the window have not been
adjusted. The next time, everything is fine. I can live with that. There is still
a limit to the line length before it gets wrapped, but I haven't figured out
how those values gets calculated. On my Vista with a resolution of 1920, the
max length is 341, on my XP with 1400, the max length is 466! Changing the
screen resolution does not seem to affect the value.

2 comments:

monkeyman said...

Thanks VERY much for posting your solution!
I was already hacking the $host settings for WindowSize, with mixed results. After adding your registry hacks, my logs are now free of pesky word wraps.
It's a shame they couldn't have designed a nice product like powershell to avoid this Windows-legacy ugliness...

Conrad Braam said...

Funny how it's 2015, ( that makes it 8 years later,) and this problem is still there. I have resorted to not messign with the consoel width at all, but rather using out-string -width ($host.ui.rawui.buffersize.width -1)
Example:
ls *.png | out-string -width ($host.ui.rawui.buffersize.width -1) | write-verbose