Asking Questions: `Read-Host`
Read-Host asks a question, waits, and hands back what was typed, always as text, -MaskInput shows stars instead of the answer, and a parameter is usually the better way in, because a question needs someone at the keyboard and a script that runs on its own has no one there.
Apprentice · PowerShell · Updated
After this lesson you can
- Ask a question with Read-Host, turn the answer into a number when needed, and hide what's typed with -MaskInput
- Choose between a parameter and Read-Host for a script's input, and explain why a script that runs on its own can't ask questions
Key takeaways
- Read-Host prints a question, waits for Enter and hands back what was typed: $days = Read-Host 'How many days back should I look'.
- The answer is always text, so '7' + 1 makes 71. [int](Read-Host …) turns it into a number, and stops with an error when it isn't one.
- -MaskInput shows a star for each letter typed. The variable still holds plain text; -AsSecureString keeps a real password scrambled.
- A question needs someone at the keyboard. A script that runs on its own can't answer one, so give it parameters, and ask only when a person is there.
An installer asks "Do you want to continue? (Y/N)". A sign-up script at a library desk asks for your name. Sometimes the person running your script isn't you: your brother starts the backup from a desktop shortcut (Run It from Anywhere), and he's never going to type -Days 7. A script can ask him instead, in plain words, and wait for the answer. That's Read-Host. It's also worth knowing when not to ask, because a question is the one thing that can stop a script at 2 a.m. with nobody there to answer it.
This lesson uses the smart-backup folder and Backup-Folder.ps1 from Parameters: param().
A question and its answer: Read-Host
Read-Host (read "read host": the host is the window you type in) prints a question and waits. Whatever is typed before Enter is the answer, which you keep in a variable:
$days = Read-Host 'How many days back should I look'
"Looking back $days days."
How many days back should I look: 7
Looking back 7 days.
PowerShell adds the colon and the space; the 7 is what was typed. The answer is always text, a string, even when it looks like a number, so adding to it joins instead of adds:
$days.GetType().Name
$days + 1
String
71
'7' + 1 is 71, as Numbers, Dates and Types showed. Put [int] in front to turn the answer into a number. When the answer isn't one, it stops there with an error:
$days = [int](Read-Host 'How many days back should I look')
$days + 1
How many days back should I look: 7
8
How many days back should I look: a week
InvalidArgument: Cannot convert value "a week" to type "System.Int32". Error: "The input string 'a week' was not in a correct format."
A script that asks
Save these lines as Ask-Backup.ps1. It asks first, then runs Backup-Folder.ps1 with the answer as its -Days:
# Ask-Backup.ps1: asks how far back to look, then runs the backup.
$days = [int](Read-Host 'How many days back should I look')
.\Backup-Folder.ps1 -Days $days
.\Ask-Backup.ps1
How many days back should I look: 7
Days looked back: 7. Files copied: 3.
One script running another, each doing one job, is how small applications grow.
A hidden answer: -MaskInput
Some answers shouldn't show on the screen. -MaskInput prints a star for each letter typed:
$password = Read-Host 'Password for the backup drive' -MaskInput
$password
Password for the backup drive: *******
tulip42
The stars stop someone reading over your shoulder, but the variable holds plain text, as the second line shows. For a real password, -AsSecureString keeps it scrambled even inside PowerShell, and commands that sign in to something take it in that form. Windows PowerShell 5.1 has no -MaskInput, only -AsSecureString; on a Mac or Linux, both work the same.
Why a parameter is usually better
A question needs someone at the keyboard. Scripts that run on their own, such as a backup that runs every night, are started with pwsh -NonInteractive, which tells PowerShell nobody is there to answer. Here's what happens to Ask-Backup.ps1:
pwsh -NonInteractive -File .\Ask-Backup.ps1
Read-Host: C:\Users\Sam\terminal-practice\smart-backup\Ask-Backup.ps1:2
Line |
2 | $days = [int](Read-Host 'How many days back should I look')
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| PowerShell is in NonInteractive mode. Read and Prompt functionality is not available.
Days looked back: 0. Files copied: 0.
The question failed, $days was empty, [int] made that 0, and the backup carried on and copied nothing. Nobody saw the red text. .\Backup-Folder.ps1 -Days 7 has none of these problems. A parameter:
- can be typed ahead, so the script runs with nobody watching;
- is checked by its type before the script starts;
- is completed by Tab and listed by
Get-Help; - can be handed over by another script, the way
Ask-Backup.ps1hands over-Days.
So give a script parameters for everything it needs, and keep Read-Host for the moments a person is sure to be there: a menu (Many Choices: switch), or "Are you sure?" before something big. For a value that must be there, a Mandatory parameter gives you both: it's typed ahead when it can be, and asked for only when it's missing.
Try it. Write Ask-Club.ps1, which asks Which folder should I back up and passes the answer to Backup-Folder.ps1 as -Path. Answer club. Then answer clb, and read what happens.
Check yourself
2 questions to try on the lesson page.
So what
Read-Host lets a script talk to the person running it. Parameters let it run without one. Ask only when someone is sure to be there to answer.