PoshRSJob
PoshRSJob copied to clipboard
Start-RSJob not honouring current console width
Do you want to request a feature or report a bug? Bug? (might be considered an enhancement?)
What is the current behavior? If I run my script, the log file lines are not wrapped. If I call the script from Start-RSJob, the log file lines are wrapped.
If the current behavior is a bug, please provide the steps to reproduce and if possible a minimal demo of the problem See below.
What is the expected behavior? Same log output whether the script is run from the console or from Start-RSJob.
Which versions of Powershell and which OS are affected by this issue? Did this work in previous versions of our scripts? PSv2 Win7
Please provide a code example showing the issue, if applicable: I've attached three files. Save them to C:\Temp (or edit test_RSjob.ps1):
test.ps1.txt - Sample script, excerpt from my larger program test_RSjob.ps1.txt - Calls test.ps1 via Start-RSJob. test_RSjob.log.txt - Log file showing before and after output from my machine (log file is appended).
test.ps1.txt test_RSJob.log.txt test_RSjob.ps1.txt
My console size is 200 x 80.
The line wrapping is kind of a big deal because the actual code has replaceable parameters ({0}, {1}, etc). I sometimes need to cut-and-paste the log lines into SQL Server Management Studio for debugging. The line wrapping results in invalid SQL unless I manually remove the extra CR's.
Your script use Format-Table | Out-String
, so it's may be Format-Table problem.
Afaik, all of out-host-like cmdlets inherit the dimensions from the parent window.
Some time ago I wrote generic logging script, using start-transcript
for saving all of script output including verbose.
I want avoid line wrapping and set console window to it maximal size.
$console = $host.UI.RawUI
$bufSize = $console.BufferSize
$bufSize.Width = $host.UI.RawUI.MaxPhysicalWindowSize.Width
$console.BufferSize = $bufSize
$console.WindowSize = $console.MaxPhysicalWindowSize
for standart output it helps, but write-verbose
still wrap it's output to width of original console!
Seems, new internal runspace buffers also get it's size from some defaults. because inside runspace $console.BufferSize -eq $console.WindowSize -eq $null
so, I suggest you remove any console interacting cmdlets from your script and use select-object
.
This way you can't get table output format, but list for logging even better :)
Thanks. Yes I've used the above code in the past myself.
As a quick test (2-3 mins), can you do the following:
Open a PS console window. Change its width to something "large" (again mine is 200 x 80) Run test.ps1 Run test_RSJob.ps1 Review test_RSjob.log.
If the two outputs are different with respect to line wrapping, what would you expect them to be?
I haven't dug through the PoshRSJob code, but I wonder if the .Net class(es) is creating a "virtual console" (probably not the right term but hopefully gets the idea across) that has a line width of 80, rather than setting it to the size of the calling console window.
If that's the case, then what do you think it should do? IMO, it would be best if the "normal" invocation and the RSJob invocation created the same results.
Thanks...
Tests are different. "Normal" run gets the same width with the console running, the rsjob test gives a 122-character result. Regardless of the current font. This value is different from yours. Even more, PsISE gives the same 122-width results.
I know how RSJob works, so I can say that it does not create some "internal console", it creates a runpace
, runs a script in it and Receive-RSJob
, receives content from all internal PS streams and writes them to corresponding streams in the current host.
When the work is completed, your data is already in the output
stream in formatted string form, due to * | Table Format | Out-string
, so the formatting remains untouched by rsjob, powershell internally formats it.
Therefore, for the same results, try to avoid the cmdlets interacting with the host inside the "hostless" runspace.
For example, try to output full object and format it after Receive-RSJob
"it creates a runpace, runs a script in it and Receive-RSJob, receives content from all internal PS streams and writes them to corresponding streams in the current host."
Can you change the RSJobs code to setup the runspace and internal PS streams to have the same column width as the invoking console?
"For example, try to output full object and format it after Receive-RSJob"
This isn't going to happen. All formatting is done by the invoked script; I will not be moving that code to the RSJobs wrapper script.
Once more:
- RSJob do nothing with "console" and
$Host.UI.RawUI.MaxWindowSize -eq $null
inside runspace. - data streams doesnt have any width, data streams contains objects, not textual strings eveny it object have string type
- these objects formatted internally by powershell when converted to string objects.
You can ask why it doesn't use calling console sizes here https://github.com/PowerShell/PowerShell but I predict 'by design' answer. Mainly because runspace doesn't have any console related properties
PS C:\> $RunspacePool = [RunspaceFactory ]::CreateRunspacePool(1, 5)
PS C:\> $RunspacePool | gm
TypeName: System.Management.Automation.Runspaces.RunspacePool
Name MemberType Definition
---- ---------- ----------
StateChanged Event System.EventHandler`1[System.Management.Automation.Runspaces.RunspacePoolStateChanged...
BeginClose Method System.IAsyncResult BeginClose(System.AsyncCallback callback, System.Object state)
BeginConnect Method System.IAsyncResult BeginConnect(System.AsyncCallback callback, System.Object state)
BeginDisconnect Method System.IAsyncResult BeginDisconnect(System.AsyncCallback callback, System.Object state)
BeginOpen Method System.IAsyncResult BeginOpen(System.AsyncCallback callback, System.Object state)
Close Method void Close()
Connect Method void Connect()
CreateDisconnectedPowerShells Method System.Collections.ObjectModel.Collection[powershell] CreateDisconnectedPowerShells()
Disconnect Method void Disconnect()
Dispose Method void Dispose(), void IDisposable.Dispose()
EndClose Method void EndClose(System.IAsyncResult asyncResult)
EndConnect Method void EndConnect(System.IAsyncResult asyncResult)
EndDisconnect Method void EndDisconnect(System.IAsyncResult asyncResult)
EndOpen Method void EndOpen(System.IAsyncResult asyncResult)
Equals Method bool Equals(System.Object obj)
GetApplicationPrivateData Method psprimitivedictionary GetApplicationPrivateData()
GetAvailableRunspaces Method int GetAvailableRunspaces()
GetCapabilities Method System.Management.Automation.Runspaces.RunspacePoolCapability GetCapabilities()
GetHashCode Method int GetHashCode()
GetMaxRunspaces Method int GetMaxRunspaces()
GetMinRunspaces Method int GetMinRunspaces()
GetType Method type GetType()
Open Method void Open()
SetMaxRunspaces Method bool SetMaxRunspaces(int maxRunspaces)
SetMinRunspaces Method bool SetMinRunspaces(int minRunspaces)
ToString Method string ToString()
ApartmentState Property System.Threading.ApartmentState ApartmentState {get;set;}
CleanupInterval Property timespan CleanupInterval {get;set;}
ConnectionInfo Property System.Management.Automation.Runspaces.RunspaceConnectionInfo ConnectionInfo {get;}
InitialSessionState Property initialsessionstate InitialSessionState {get;}
InstanceId Property guid InstanceId {get;}
IsDisposed Property bool IsDisposed {get;}
RunspacePoolAvailability Property System.Management.Automation.Runspaces.RunspacePoolAvailability RunspacePoolAvailabil...
RunspacePoolStateInfo Property System.Management.Automation.RunspacePoolStateInfo RunspacePoolStateInfo {get;}
ThreadOptions Property System.Management.Automation.Runspaces.PSThreadOptions ThreadOptions {get;set;}
PS C:\> $powershell = [powershell]::Create()
PS C:\> $powershell.RunspacePool = $RunspacePool
PS C:\> $powershell | gm
TypeName: System.Management.Automation.PowerShell
Name MemberType Definition
---- ---------- ----------
InvocationStateChanged Event System.EventHandler`1[System.Management.Automation.PSInvocationStateChangedEventArgs] Invoca...
AddArgument Method powershell AddArgument(System.Object value)
AddCommand Method powershell AddCommand(string cmdlet), powershell AddCommand(string cmdlet, bool useLocalScop...
AddParameter Method powershell AddParameter(string parameterName, System.Object value), powershell AddParameter(...
AddParameters Method powershell AddParameters(System.Collections.IList parameters), powershell AddParameters(Syst...
AddScript Method powershell AddScript(string script), powershell AddScript(string script, bool useLocalScope)
AddStatement Method powershell AddStatement()
AsJobProxy Method System.Management.Automation.PSJobProxy AsJobProxy()
BeginInvoke Method System.IAsyncResult BeginInvoke(), System.IAsyncResult BeginInvoke[T](System.Management.Auto...
BeginStop Method System.IAsyncResult BeginStop(System.AsyncCallback callback, System.Object state)
Connect Method System.Collections.ObjectModel.Collection[psobject] Connect()
ConnectAsync Method System.IAsyncResult ConnectAsync(), System.IAsyncResult ConnectAsync(System.Management.Autom...
CreateNestedPowerShell Method powershell CreateNestedPowerShell()
Dispose Method void Dispose(), void IDisposable.Dispose()
EndInvoke Method System.Management.Automation.PSDataCollection[psobject] EndInvoke(System.IAsyncResult asyncR...
EndStop Method void EndStop(System.IAsyncResult asyncResult)
Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode()
GetType Method type GetType()
Invoke Method System.Collections.ObjectModel.Collection[psobject] Invoke(), System.Collections.ObjectModel...
Stop Method void Stop()
ToString Method string ToString()
Commands Property System.Management.Automation.PSCommand Commands {get;set;}
HadErrors Property bool HadErrors {get;}
HistoryString Property string HistoryString {get;set;}
InstanceId Property guid InstanceId {get;}
InvocationStateInfo Property System.Management.Automation.PSInvocationStateInfo InvocationStateInfo {get;}
IsNested Property bool IsNested {get;}
IsRunspaceOwner Property bool IsRunspaceOwner {get;}
Runspace Property runspace Runspace {get;set;}
RunspacePool Property System.Management.Automation.Runspaces.RunspacePool RunspacePool {get;set;}
Streams Property System.Management.Automation.PSDataStreams Streams {get;}
even non-public
PS C:\> $Flags = 'nonpublic','instance','static'
PS C:\> $RunspacePool.gettype().GetProperties($flags) | Select-Object -ExpandProperty Name
IsRemote
RemoteRunspacePoolInternal
PS C:\> $RunspacePool.gettype().GetMethods($flags) | Select-Object -ExpandProperty Name
get_IsRemote
get_RemoteRunspacePoolInternal
add_InternalStateChanged
add_InternalForwardEvent
remove_InternalStateChanged
remove_InternalForwardEvent
add_InternalRunspaceCreated
remove_InternalRunspaceCreated
OnStateChanged
add_ForwardEvent
remove_ForwardEvent
OnInternalPoolForwardEvent
OnEventForwarded
add_RunspaceCreated
remove_RunspaceCreated
OnRunspaceCreated
BeginGetRunspace
CancelGetRunspace
EndGetRunspace
ReleaseRunspace
AssertPoolIsOpen
Finalize
MemberwiseClone
PS C:\> $Powershell.gettype().GetProperties($flags) | Select-Object -ExpandProperty Name
ErrorBuffer
ProgressBuffer
VerboseBuffer
DebugBuffer
WarningBuffer
InformationBuffer
InformationalBuffers
RedirectShellErrorOutputPipe
IsChild
EndInvokeAsyncResult
ErrorBufferOwner
OutputBufferOwner
OutputBuffer
IsGetCommandMetadataSpecialPipeline
RemotePowerShell
ExtraCommands
RunningExtraCommands
PS C:\> $Powershell.gettype().GetMethods($flags) | Select-Object -ExpandProperty Name
get_ErrorBuffer
set_ErrorBuffer
get_ProgressBuffer
set_ProgressBuffer
get_VerboseBuffer
set_VerboseBuffer
set_DebugBuffer
get_DebugBuffer
set_WarningBuffer
get_WarningBuffer
get_InformationBuffer
set_InformationBuffer
get_InformationalBuffers
get_RedirectShellErrorOutputPipe
set_RedirectShellErrorOutputPipe
get_IsChild
set_EndInvokeAsyncResult
get_EndInvokeAsyncResult
set_IsRunspaceOwner
set_ErrorBufferOwner
get_ErrorBufferOwner
set_OutputBufferOwner
get_OutputBufferOwner
get_OutputBuffer
get_IsGetCommandMetadataSpecialPipeline
set_IsGetCommandMetadataSpecialPipeline
get_RemotePowerShell
get_ExtraCommands
get_RunningExtraCommands
InitForRemotePipeline
InitForRemotePipelineConnect
Create
AddCommand
SetHadErrors
add_RunspaceAssigned
remove_RunspaceAssigned
SetRunspace
GetRunspaceConnection
CheckRunspacePoolAndConnect
InvokeWithDebugger
BeginBatchInvoke
BatchInvocationWorkItem
BatchInvocationCallback
DoRemainingBatchCommands
DetermineIsBatching
SetupAsyncBatchExecution
EndAsyncBatchExecution
AppendExceptionToErrorStream
PipelineStateChanged
GenerateNewInstanceId
GetSteppablePipeline
GetContextFromTLS
GetSteppablePipeline
IsCommandRunning
IsDisconnected
AssertExecutionNotStarted
AssertChangesAreAccepted
AssertNotDisposed
Dispose
InternalClearSuppressExceptions
RaiseStateChangeEvent
SetStateChanged
CloseInputBufferOnReconnection
ClearRemotePowerShell
SetIsNested
CoreInvoke
CoreInvokeHelper
CoreInvokeRemoteHelper
CoreInvoke
CoreInvokeAsync
VerifyThreadSettings
Prepare
CoreStop
ReleaseDebugger
StopHelper
StopThreadProc
ServerSupportsBatchInvocation
AddToRemoteRunspaceRunningList
RemoveFromRemoteRunspaceRunningList
GetRemoteRunspacePoolInternal
FromPSObjectForRemoting
ToPSObjectForRemoting
CommandsAsListOfPSObjects
SuspendIncomingData
ResumeIncomingData
WaitForServicingComplete
AsPSPowerShellPipeline
Finalize
MemberwiseClone
there is a method of runspace creathion that uses host
static System.Management.Automation.Runspaces.RunspacePool CreateRunspacePool(int minRunspaces, int maxRunspaces, System.Management.Automation.Host.PSHost host)
but for this method you should use your own System.Management.Automation.Host.PSHost class.
It's quite difficult to use it and nobody will want to implement it for poorly designed script