ChatGPT解决这个技术问题 Extra ChatGPT

Prompt for user input in PowerShell

I want to prompt the user for a series of inputs, including a password and a filename.

I have an example of using host.ui.prompt, which seems sensible, but I can't understand the return.

Is there a better way to get user input in PowerShell?


P
Peter Mortensen

Read-Host is a simple option for getting string input from a user.

$name = Read-Host 'What is your username?'

To hide passwords you can use:

$pass = Read-Host 'What is your password?' -AsSecureString

To convert the password to plain text:

[Runtime.InteropServices.Marshal]::PtrToStringAuto(
    [Runtime.InteropServices.Marshal]::SecureStringToBSTR($pass))

As for the type returned by $host.UI.Prompt(), if you run the code at the link posted in @Christian's comment, you can find out the return type by piping it to Get-Member (for example, $results | gm). The result is a Dictionary where the key is the name of a FieldDescription object used in the prompt. To access the result for the first prompt in the linked example you would type: $results['String Field'].

To access information without invoking a method, leave the parentheses off:

PS> $Host.UI.Prompt

MemberType          : Method
OverloadDefinitions : {System.Collections.Generic.Dictionary[string,psobject] Pr
                    ompt(string caption, string message, System.Collections.Ob
                    jectModel.Collection[System.Management.Automation.Host.Fie
                    ldDescription] descriptions)}
TypeNameOfValue     : System.Management.Automation.PSMethod
Value               : System.Collections.Generic.Dictionary[string,psobject] Pro
                    mpt(string caption, string message, System.Collections.Obj
                    ectModel.Collection[System.Management.Automation.Host.Fiel
                    dDescription] descriptions)
Name                : Prompt
IsInstance          : True

$Host.UI.Prompt.OverloadDefinitions will give you the definition(s) of the method. Each definition displays as <Return Type> <Method Name>(<Parameters>).


Thanks, @Rynant. Accepted answer for being the only one who actually answered my main question! ;) All the other information is really helpful too, especially as I'm still groping my way in PS.
No problem, @AJ. Another way to get information about a method is to leave off the parentheses. I'll add an example to my answer.
FYI you can also use Get-Credential if you're getting usernames and passwords.
P
Peter Mortensen

Using parameter binding is definitely the way to go here. Not only is it very quick to write (just add [Parameter(Mandatory=$true)] above your mandatory parameters), but it's also the only option that you won't hate yourself for later.

More below:

[Console]::ReadLine is explicitly forbidden by the FxCop rules for PowerShell. Why? Because it only works in PowerShell.exe, not PowerShell ISE, PowerGUI, etc.

Read-Host is, quite simply, bad form. Read-Host uncontrollably stops the script to prompt the user, which means that you can never have another script that includes the script that uses Read-Host.

You're trying to ask for parameters.

You should use the [Parameter(Mandatory=$true)] attribute, and correct typing, to ask for the parameters.

If you use this on a [SecureString], it will prompt for a password field. If you use this on a Credential type, ([Management.Automation.PSCredential]), the credentials dialog will pop up, if the parameter isn't there. A string will just become a plain old text box. If you add a HelpMessage to the parameter attribute (that is, [Parameter(Mandatory = $true, HelpMessage = 'New User Credentials')]) then it will become help text for the prompt.


This is the most flexible and user-friendly solution, but I almost ignored your advice because there were no clear code examples like in Rynant's answer. Can you provide some nicely formatted examples?
"Read-Host is, quite simply, bad form"... unless you're using it to conditionally accept input that was left out because someone wasn't calling your script with ANY parameters. BOOM.
No: it's still bad form then. That's why you mark parameters as mandatory.
What if you want to write an interactive script? Say it is a script that only requires user input if certain conditions are met. For instance if your script is to set up a target directory for an SDK, you might want to confirm that the user wants to delete the directory if it already exists.
I think user1499731 had a good point ... There are times when you need to take input from the user that can only be meaningfully provided after some information is displayed or another operation is performed. In that case, you can't use a parameter, and the reasons given here for Read-Host being "bad form" don't apply. Moreover, .ShouldProcess() has restrictions that Read-Host doesn't, such as being limited to just a few answers. However I agree that .ShouldProcess() is better, when it's applicable.
A
ANeves

Place this at the top of your script. It will cause the script to prompt the user for a password. The resulting password can then be used elsewhere in your script via $pw.

   Param(
     [Parameter(Mandatory=$true, Position=0, HelpMessage="Password?")]
     [SecureString]$password
   )

   $pw = [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($password))

If you want to debug and see the value of the password you just read, use:

   write-host $pw

佚名

As an alternative, you could add it as a script parameter for input as part of script execution

 param(
      [Parameter(Mandatory = $True,valueFromPipeline=$true)][String] $value1,
      [Parameter(Mandatory = $True,valueFromPipeline=$true)][String] $value2
      )