-
Notifications
You must be signed in to change notification settings - Fork 8.2k
Description
Summary of the new feature / enhancement
It seems very unintuitive and obscure to have a 1022 character input limit for Read-Host. There should at least be an option for removing the limit and/or specifying an explicit limit in newer versions of PowerShell if the reason for keeping it is for backwards-compatibility. Get-Content has no such limit and it feels like Read-Host should behave analogously. The theoretical max length of a String is 2147483647. There are many legitimate reasons for wanting to allow input of more than 1022 characters (e.g., user pasting large clipboard text, but script author not wanting to use Get-Clipboard to also allow users to manually type input).
Repro:
'x'*5000 | set-clipboard
read-host
# press CTRL-V and the contents are truncated to 1022 characters
Note that this seems to also apply to: $host.UI.ReadLine() and $z = [system.console]::ReadLine()
A related issue was discussed here, but with no resolution:
Read-Host max imput length cap in pwsh 7
Workarounds:
I finally managed to find a couple workarounds, but reading input shouldn't be this difficult. Workaround 1 has the advantage that it will work with PowerShell background jobs that require keyboard input.
Workaround 1:
<#
.SYNOPSIS
Read a line of input from the host.
.DESCRIPTION
Read a line of input from the host.
.EXAMPLE
$s = Read-HostLine -prompt "Enter something"
.NOTES
Read-Host has a limitation of 1022 characters.
This approach is safe to use with background jobs that require input.
If pasting content with embedded newlines, only the first line will be read.
A downside to the ReadKey approach is that it is not possible to easily edit the input string before pressing Enter as with Read-Host.
#>
function Read-HostLine ($prompt = $null) {
if ($prompt) {
"${prompt}: " | Write-Host
}
$str = ""
while ($true) {
$key = $host.UI.RawUI.ReadKey("NoEcho, IncludeKeyDown");
# Paste the clipboard on CTRL-V
if (($key.VirtualKeyCode -eq 0x56) -and # 0x56 is V
(([int]$key.ControlKeyState -band [System.Management.Automation.Host.ControlKeyStates]::LeftCtrlPressed) -or
([int]$key.ControlKeyState -band [System.Management.Automation.Host.ControlKeyStates]::RightCtrlPressed))) {
$clipboard = Get-Clipboard
$str += $clipboard
Write-Host $clipboard -NoNewline
continue
}
elseif ($key.VirtualKeyCode -eq 0x08) { # 0x08 is Backspace
if ($str.Length -gt 0) {
$str = $str.Substring(0, $str.Length - 1)
Write-Host "`b `b" -NoNewline
}
}
elseif ($key.VirtualKeyCode -eq 13) { # 13 is Enter
Write-Host
break
}
elseif ($key.Character -ne 0) {
$str += $key.Character
Write-Host $key.Character -NoNewline
}
}
return $str
}
Workaround 2:
$maxLength = 65536
[System.Console]::SetIn([System.IO.StreamReader]::new([System.Console]::OpenStandardInput($maxLength), [System.Console]::InputEncoding, $false, $maxLength))
$s = [System.Console]::ReadLine()
Workaround 3:
function Read-String ($maxLength = 65536) {
$str = ""
$inputStream = [System.Console]::OpenStandardInput($maxLength);
$bytes = [byte[]]::new($maxLength);
while ($true) {
$len = $inputStream.Read($bytes, 0, $maxLength);
$str += [string]::new($bytes, 0, $len)
if ($str.EndsWith("`r`n")) {
$str = $str.Substring(0, $str.Length - 2)
return $str
}
}
}
$s = Read-String
Some issues involving background jobs with Workaround 2 and 3 that use Console directly (hence a Read-Host fix would be much better):
# This works
$j = start-job -ScriptBlock { "Got '$($Host.UI.RawUI.ReadKey())'." }
$j|rcjb -wait # Can successfully send a key to the job
# This does NOT work
$j = start-job -ScriptBlock { "Got '$([system.console]::readkey())'." }
$j|rcjb -wait # Job remains stuck waiting for input
$j = start-job -ScriptBlock {
$maxLength = 65536
[System.Console]::SetIn([System.IO.StreamReader]::new([System.Console]::OpenStandardInput($maxLength), [System.Console]::InputEncoding, $false, $maxLength))
$token = [System.Console]::ReadLine()
}
$j | stop-job # This blocks forever! with no ability to CTRL-C! The outer shell is completely locked.