0

I need to get a very long string input (around 9,000 characters), but Read-Host will truncate after around 8,000 characters. How can I extend this limit?

Lance U. Matthews
  • 15,725
  • 6
  • 48
  • 68
xinglong
  • 115
  • 1
  • 5
  • 4
    this sounds like a classic `x, y` problem where you are really working on `x`, but asking about your sub-solution `y`. **what is the problem that causes you to want to use a _keyboard input technique_ to feed a multi-thousand character string into a script?** – Lee_Dailey Feb 03 '20 at 19:35
  • I am implementing a powershell script tool for my project, the input usually copy to clipboard and past to this tool, the clipboard can contains more than 9000 charaters but the powershell read-host cannot. – xinglong Feb 05 '20 at 00:53
  • ah! thank you for the info. [*grin*] have you looked at `Get-Help *clip*` yet? those tow cmdlets provide fairly direct access to the system clipboard. i do not know what the character count limit is for either, tho. – Lee_Dailey Feb 05 '20 at 01:21
  • thanks for the replying, but I need to past and do some modification before hit "enter" and input to the script tool, "read-host" is the most prefer way to do this, but unfortunately it has maximum size limit. – xinglong Feb 05 '20 at 02:03
  • what you are saying makes no sense to me ... but i presume there is something that you left out. the `Get-Clipboard` cmdlet looks like it would be worth attempting to use. you CANNOT change `Read-Host` unless you recompile the source for PoSh. – Lee_Dailey Feb 05 '20 at 08:36

1 Answers1

0

The following are possible workarounds. Workaround 1 has the advantage that it will work with PowerShell background jobs that require keyboard input. Note that if you are trying to paste clipboard content containing new lines, Read-HostLine will only read the first line, but Read-Host has this same behavior.

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-Line($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-Line

More discussion here:
Console.ReadLine() max length?
Why does Console.Readline() have a limit on the length of text it allows?
https://github.com/PowerShell/PowerShell/issues/16555

KrisG
  • 119
  • 5
  • Consider `Read-String 5 <# Enter '1234567890' #>; Read-String 5` with the `SetIn`-less version. Both calls return `[String]`s of length `3` because, for input of `$maxLength` characters or longer, it always truncates after `$maxLength - 2` characters. Also, the second call returns `'678'` _immediately_, I think because the truncated characters from the first call are still available to be consumed from `$inputStream` in the second call. There seems to be a little variation between PowerShell 5.1 and 7.2 as to whether typed input is required before those remaining characters get consumed. – Lance U. Matthews Dec 05 '21 at 06:10
  • Also, the first version creates but does not use `$bytes`. – Lance U. Matthews Dec 05 '21 at 06:23
  • Thanks, Lance. I've updated the code to support strings longer than the buffer size and found a workaround that works with PowerShell background jobs that require input. – KrisG Dec 05 '21 at 10:14