craig
craig

Reputation: 26262

Use Read-Host to enter multiple lines

Is there a way to use the Read-Host cmdlet to capture multiple lines?

My current approach is a bit awkward (and doesn't really work):

PS> Send-MailMessage -SmtpServer 'smtp.domain.xxx' -From 'First Last <[email protected]>' -To 'First Last <[email protected]>' -Subject 'Testing' -Body (Read-Host 'Enter text')
Enter text: line one `n line two `n line three

Resulting email body isn't three lines:

line one `n line two `n line three

Upvotes: 5

Views: 8965

Answers (5)

D.Mountain
D.Mountain

Reputation: 11

There's no way to simply use Read-Host, you need to get creative.

I have a overly-complex function that solves this. It exits either on a special key combination (Ctrl + Q), 3 line breaks, or about 5 seconds of inactivity.

It's based on the script provided by "Elavarasan Muthuvalavan - Lee" here: Waiting for user input with a timeout

function Read-HostTimed {
    [CmdletBinding()]
    param (
        [Parameter(Position = 0, Mandatory = $false, ValueFromPipeline = $true)]
        [string]$Prompt  
    )
    Begin {
        $lines = ''
        $count = 0
        $sleepTimer = 5 # in milliseconds
        $loopCount = 300
        $coefficient = 3.33333 # just a hip-shot value to take into account the time taken by the loop to execute.
        $seconds = [math]::Round((($loopCount * $sleepTimer) * $coefficient) / 1000)
        $QuitKey = 81 # Character code for 'q' key.
        $exitKeyCombo = "Ctrl + 'q'"
        $exitOnNReturns = 2 # exits on n + 1 returns
        Write-Host ("Input completes in ${seconds}s or when pressing $exitKeyCombo!")
    }
    Process {
        if ($Prompt) {
            Write-Host "${Prompt}: " -NoNewline
        }
        while ($count -lt $loopCount) {
            if ($host.UI.RawUI.KeyAvailable) {
                $key = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyUp")
                # For Key Combination: eg., press 'LeftCtrl + q' to quit.
                if (($key.VirtualKeyCode -eq $QuitKey) -and ($key.ControlKeyState -eq "LeftCtrlPressed")) {
                    Write-Host -ForegroundColor Yellow ("Input saved.")
                    break
                }

                if ($key.Character -in @("`r", "`n")) {
                    Write-Host
                    if ($lines.Length -ge $exitOnNReturns -and $lines.Substring($lines.Length - $exitOnNReturns, $exitOnNReturns) -eq "`n"*$exitOnNReturns) {
                        break
                    }
                    $lines += "`n"
                }
                elseif ($key.Character -eq "`b") {
                    Write-Host -NoNewline "`b `b"
                    if ($lines) {
                        $lines = $lines.Substring(0, $lines.Length - 1)
                    }
                }
                else {
                    Write-Host $key.Character -NoNewline
                    $lines += $key.Character
                }
                
                # reset exit counter
                $count = 0
            }
            #Do operation every loop
            $count++
            Write-Verbose ("Key: " + $key.VirtualKeyCode + " | Count: " + $count)
            Start-Sleep -Milliseconds $sleepTimer
        }
    }
    End {
        return $lines.trim()
    }
}

Upvotes: 1

GaTechThomas
GaTechThomas

Reputation: 6113

A lightly optimized function to read multi-line input into a string:

function ReadMultiLineInput() {
    # optimize capturing the first line
    $inputText = Read-Host
    if (!$inputText) { return "" }

    # capture each subsequent line
    while ($r = Read-Host) {
        # exit when an empty line is entered
        if (!$r) { break }

        $inputText += "`n$r"
    }

    return $inputText
}

Example usage to output a comma-separated string:

$multilineInput = ReadMultiLineInput
$csvInput = $multilineInput -replace "`n", ","

Upvotes: 1

majkinetor
majkinetor

Reputation: 9056

$x = while (1) { read-host | set r; if (!$r) {break}; $r}

empty line finishes the input.

Upvotes: 7

Nico Nekoru
Nico Nekoru

Reputation: 3112

I know this is late, but I really liked @majkinetor's answer but it still needed improvement as input wouldn't be taken. I improved the answer like this:

while (1) 
  {
    read-host | set r
    set test -value ($test+"`n"+$r)
    if (!$r) {break}
  }
$test = $test.trim()

It does the same thing pretty much, with $r being each line of input, but then it adds it to variable $test. This will actually save the input as well. If you want to allow line breaks, I would just add

$test = $test.replace("<br>","")

Which would make <br> as a carriage return, but you can change it to anything you's like like so:

$test = $test.replace("Whatever you want","")
$test = $test.replace("CRLF","")
$test = $test.replace("Break","")

Upvotes: 4

Mark
Mark

Reputation: 1098

Try something like this where you just have (Read-Host)

(@(While($l=(Read-Host).Trim()){$l}) -join("`n"))

You exit the loop with a blank/white-space only line.

You can use $_ rather than a real variable but I find that "creepy" so I don't do it.

Upvotes: 4

Related Questions