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.ps1 hands 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.

Key terms

Cheat sheets

Related