The immediate answer to your question is that you need to run your keystroke-sending code in parallel to your ./Script1.ps1
invocation, given that ./Script.ps1
is a synchronous call whose Read-Host
call blocks further processing until a response has been provided.
However, as kconsiglio points, in your simple case it may be sufficient to simply send the keystrokes ahead of time, relying on them to be buffered and later read by Read-Host
, which doesn't require parallelism:
# Send the keystrokes and rely on their getting buffered,
# so that the subsequently launched ./Script.ps1 reads them.
(New-Object -ComObject Wscript.Shell).SendKeys('myPassword{ENTER}')
./Script.ps1
You may need parallelism for more sophisticated interaction with the interactive script, in which case you can use Start-Job
or, preferably, Start-ThreadJob
(available by default in PowerShell (Core) 7+, installable on demand in Windows PowerShell) for this parallel execution:
$Title = "bullshit"
$host.UI.RawUI.WindowTitle = $Title
# Launch the keystroke-sending code in parallel, in the background.
# Note: If Start-ThreadJob is available / installed, use it instead.
$jb = Start-Job {
$wshell = New-Object -ComObject wscript.shell;
$wshell.AppActivate('bullshit')
Sleep 1
$wshell.SendKeys('myPassword{ENTER}')
}
$Name = Read-Host -Prompt 'Input your name'
Write-Host "Your name is '$Name'"
Remove-Job -Force $jb
However, sending keystrokes is always brittle (can break), isn't always an option (e.g., in hidden scheduled tasks), and is best avoided if robust alternatives are available:
You can automate input to Read-Host
in one of two ways:
Call the target script via the PowerShell CLI, in which case Read-Host
responses can be provided via the pipeline (stdin) - notably, this does not work in-session:
# Note: Use 'pwsh.exe' if you're running PowerShell (Core) 7+
'myPassword' | powershell.exe -File ./Script.ps1
- The potential down-side (apart from a performance hit due to launching another PowerShell instance) is that output from
./Script.ps1
will be text only.
Define a proxy Read-Host
function that overrides the built-in cmdlet and automatically returns the desired string instead of prompting:
function Read-Host { 'mypassword' }
./Script.ps1
Remove-Item function:Read-Host
This technique was borrowed from this answer, which also shows how to make the automated responses conditional based on the prompt string.
The advantage is in-process execution, with preservation of type fidelity in the script's output.
Note that you only need to return the actual desired response, such as 'mypassword'
- there is no simulation of keystrokes involved here, and therefore no need for key specifiers such as {ENTER}
(which only the .SendKeys()
method would understand).