Reputation: 26262
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
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
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
Reputation: 9056
$x = while (1) { read-host | set r; if (!$r) {break}; $r}
empty line finishes the input.
Upvotes: 7
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
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