1

I have two scripts:

Script1 and Script2

Script1 contains:

$Title = "bullshit"
$host.UI.RawUI.WindowTitle = $Title    

$Name = Read-Host -Prompt 'Input your  name'
Write-Host "Your name is '$Name'" 

Script2 contains:

./Script1.ps1

What I need is to run Script2, which will run Script1, and once that script asks for user input, Script2 would just press enter, and let it continue.

I tried solution mentioned here, but it doesn´t work - it keeps waiting for user input, and only after you manually press enter, it will produce keypress (enter).

Other proposed solution for Script2 I found has the same problem as the aforementioned one:

./Script1.ps1

$wshell = New-Object -ComObject wscript.shell;
$wshell.AppActivate('bullshit')
Sleep 2
$wshell.SendKeys('myPassword{ENTER}')

Do you know what I am doing wrong? How can I make the Script2 press enter, once Script1 asks for it?

mklement0
  • 382,024
  • 64
  • 607
  • 775
Blu3Rat
  • 49
  • 7
  • Try double quotes instead of single quotes : "myPassword{ENTER}" – jdweng Apr 03 '23 at 15:05
  • Would putting `./Script1.ps1` beneath `$wshell.SendKeys('myPassword{ENTER}')` work for you? Just tested and it does send input. You just need to call the script after the rest of the code. – kconsiglio Apr 03 '23 at 15:34
  • @jdweng, There is _no_ difference between `'myPassword{ENTER}'` and `"myPassword{ENTER}"`, given that the string content contains no `$` symbols that would be subject to string expansion (interpolation) in `"..."` strings. Your comment is not only irrelevant to the question at hand and therefore a distraction, it is a _confusing_ distraction. – mklement0 Apr 03 '23 at 16:11
  • @mklement0 : Isn't {Enter} a keyboard entry that needs to be replaced? – jdweng Apr 03 '23 at 16:50
  • @jdweng, `{ENTER}` is _unrelated to PowerShell's string expansion_ (which, as noted, only acts on `$`-prefixed tokens). It is part of the syntax understood by `.SendKeys()` and must therefore be _passed through to it_, as-is. – mklement0 Apr 03 '23 at 16:56

1 Answers1

2

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).

mklement0
  • 382,024
  • 64
  • 607
  • 775
  • Thank you for the exhausting answer :) kconsiglio´s solution worked, but I would like to utilize the **Define proxy** solution, because of the advantages you mentioned. I have one problem with it though: When I try to apply it: `function Read-Host { '{ENTER}' } ./Script1.ps1 Remove-Item function:Read-Host` What happens is it will send the enter as a string, instead of interpreting it as "hit the enter key." So the output looks like this: `Your name is '{ENTER}'` instead of this: `Your name is ''` – Blu3Rat Apr 04 '23 at 11:23
  • I tried it with both variants: `function Read-Host { '{ENTER}' } / function Read-Host { '~' }` – Blu3Rat Apr 04 '23 at 11:23
  • Found it! The solution was to use dollar sign: `function Read-Host { "${ENTER}" }` – Blu3Rat Apr 04 '23 at 11:37
  • @Blu3Rat, `"${ENTER}"` is the same as `''`, i.e. the _empty string_ (unless you define an `$ENTER` == `${ENTER}` variable first), thanks to PowerShell's string interpolation. That is, all you need is `function Read-Host { '' }` in order to get the equivalent of just pressing ENTER in a real `Read-Host` call, thereby returning the empty string. There is no sending of keystrokes involved here (`{ENTER}` is syntax only understood by `.SendKeys()`). I've also updated the answer to make that clearer. – mklement0 Apr 04 '23 at 12:09