'Max size of ScriptBlock / InitializationScript for Start-Job in PowerShell
When you start a new job with Start-Job
, you can pass it a ScriptBlock
and a InitializationScript
for example:
Function FOO {
Write-Host "HEY"
}
Start-Job -ScriptBlock {FOO} -InitializationScript {
Function Foo { $function:FOO }
} | Wait-Job | Receive-Job
There seems to be a limit to the size of the initialization script you can pass, if it is too big then you get an error such as
[localhost] An error occurred while starting the background process. Error reported: The filename or extension is too long. + CategoryInfo : OpenError: (localhost:String) [], PSRemotingTransportException + FullyQualifiedErrorId : -2147467259,PSSessionStateBroken
Behind the scenes, PowerShell is creating a new process and passing InitializationScript
as a Base64 encoded command line parameter.
According to the Win32 CreateProcess()
function, the max size of the command ine is 32,768 characters. So obviously if your Base64 encoded InitializationScript
is getting near this size then you will probably get an error.
I haven't yet found a limit for the size of the ScriptBlock
parameter. Can someone confirm that there is no limit?
I assume that there is no limit because it looks like the ScriptBlock
is transmitted to the child process via standard input?
Solution 1:[1]
Your guess was correct.
PowerShell translates a Start-Job
call into a PowerShell CLI call behind the scenes (to powershell.exe
for Windows PowerShell, and to pwsh
for PowerShell (Core) 7+), that is, it achieves parallelism via a child process that the calling PowerShell session communicates with, using the standard in- and output streams:
Because the
-InitializationScript
script block is translated to a Base64-encoded string representing the bytes of the UTF-16LE encoding of the block's string representation, which is passed to the CLI's-EncodedCommand
parameter, its max. length is limited by the overall length limit of a process command line.That limit is 32,766 characters (Unicode characters, not just bytes), because a terminating NUL character is required in the underlying WinAPI call (to quote from the
CreateProcess()
WinAPI function documentation you link to: "The maximum length of this string is 32,767 characters, including the Unicode terminating null character").Note that the full path of the PowerShell executable is included in this limit, in double-quoted form (see below), and it really is the entire resulting command line that matters; therefore, given that PowerShell (Core) 7+ can be installed in any directory, its installation location has an effect on the effective limit, as does the length of the path of the current directory (see next point).
In Windows PowerShell, whose location is fixed, and whose CLI parameter values are of fixed length in the invocation (see below), this leaves 32,655 characters for the Base64-encoded string (32766 - 111 characters for the executable path and fixed parameters and the
-EncodedCommand
parameter name); while similar, no fixed number can be given for PowerShell (Core) 7+, due to differing install locations and the length of the-wd
(working-directory) argument depending on the current location.Base64-encoding the bytes of a UTF-16LE-encoded strings results in a ca. 2.67-fold increase in length, which*makes the **max. length of a script block passed to
-InitializationScript
12,244 characters[1] for Windows PowerShell; for PowerShell (Core) 7+, it'll be slightly lower, depending on the installation location and the length of the current directory's path.
By contrast, the
-ScriptBlock
argument, i.e. the operation to perform in the background, is sent via stdin (the standard input stream) to the newly launched PowerShell process, and therefore has no length limit.
For instance, the following Start-Job
call:
Start-Job -ScriptBlock { [Environment]::CommandLine } -InitializationScript { 'hi' > $null } |
Receive-Job -Wait -AutoRemoveJob
reveals that the background-job child process was launched as follows, when run from Windows PowerShell:
"C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" -Version 5.1 -s -NoLogo -NoProfile -EncodedCommand IAAnAGgAaQAnACAAPgAgACQAbgB1AGwAbAAgAA==
As you can see, the -ScriptBlock
argument's text is not present in the resulting command line (it was sent via stdin), whereas the -InitializationScript
argument's is, as the Base64-encoded string passed to -EncodedCommand
, which you can verify as follows:
# -> " 'hi' > $null ", i.e. the -InitializationScript argument, sans { and }
[Text.Encoding]::Unicode.GetString([Convert]::FromBase64String('IAAnAGgAaQAnACAAPgAgACQAbgB1AGwAbAAgAA=='))`)
As for the other parameters:
-s
is short for-servermode
, and it is an undocumented parameter whose sole purpose is to facilitate background jobs (communication with the calling process via its standard streams); see this answer for more information.-Version 5.1
applies only to Windows PowerShell, and isn't strictly necessary.-NoLogo
is also not strictly necessary, because it is implied by the use of-EncodedCommand
(as it would be with-Command
and-File
).In PowerShell (Core) 7+, you'd also see a
-wd
(short for:-WorkingDirectory
) parameter, because background jobs there now sensibly use the same working directory as the caller.
[1]
[Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes('x' * 12244)).Length
yields 32652
, which is the closest you can come to the 32655
limit; an input length of 12245
yields 32656
.
Solution 2:[2]
the script block actually have limit. and you can run command with scriptblock with 3 way :
$scriptblock = '
get-help
get-command
dir
get-help *command*
get-command *help*
'
iex $scriptblock
or use it :
$scriptblock = {
get-help
get-command
dir
get-help *command*
get-command *help*
}
Start-Process powershell.exe -ArgumentList "Command $scriptblock"
or use this :
Start-Process powershell {iex '
get-help
get-command
dir
get-help *command*
get-command *help*
'
}
the limit of script that can pass to powershell.exe is 12190 bytes. but for script i never get limit from powershell more than 4000 line code.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | |
Solution 2 |