ReadLine.exe

Copyright © 2014 by Bill Stewart

Description

ReadLine.exe is a Win32 console program that you can use in a script to get input from a user. When you run ReadLine.exe, it waits for you to enter something. When you press ENTER, it writes what you typed to standard output.

Version History

1.0 (16 May 2014)

License Agreement

This program may be used freely in any environment without any payment to the author, provided you abide by the following:

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.

Background

Cmd.exe has the set /p command to read typed input into an environment variable, but it has two noticeable limitations:

  1. It doesn't allow you to pre-populate the input value. The starting input line is always empty.
  2. It doesn't provide a way for you to hide input from the screen (e.g., for entering a password).

The ReadLine.exe program lets you overcome both of these limitations.

Usage

The program's command-line syntax is as follows:

ReadLine [-p prompt] [-h] [-- initial_value]

The -p parameter specifies a prompt for the user. If it contains spaces, enclose it in quotes ("). The prompt can be up to 255 characters long and cannot contain quotes.

The -h parameter specifies hidden input. Characters are not displayed as they are entered.

You can specify an initial input value at the end of the command line after two dashes (--). The initial input value is ignored if you specify -h. The initial input value is used without any parsing or interpretation, so it can include spaces and quotes. The only limitation is that leading whitespace is ignored.

Cmd.exe Shell Script (Batch File) Examples

Prompt for Hidden Input

@echo off
for /f "delims=" %%p in ('ReadLine -h -p "Enter password: "') do set PWD=%%p
echo You entered: %PWD%

If you want to try this command at a cmd.exe prompt, use %p instead of %%p.

Prompt for Input with Initial Value

@echo off
set NAME=Gil Bates
for /f "delims=" %%n in ('ReadLine -p "Enter name: " -- %NAME%') do set NAME=%%n
echo You entered: %NAME%

Interactively Edit the Path Environment Variable

Using with PowerShell

NOTE ReadLine.exe does not work as expected in the PowerShell Integrated Scripting Environment (ISE) or other PowerShell environments that do not run as a standard Win32 console application. The notes in this section apply only to the standard console-mode version of PowerShell.

You can use ReadLine.exe in PowerShell to pre-populate an input value, but this only works as expected under the following conditions:

That is, the following example will work as expected:

$name = ReadLine -p "Enter name: " -- Gil Bates

This example will also work as expected:

$name = "GilBates"
$name = ReadLine -p "Enter name: " -- $name

However, because PowerShell will insert quotes around a variable's value if it contains spaces when passing it to ReadLine.exe, the following example does not work as expected:

$name="Gil Bates"
$name = ReadLine -p "Enter name: " -- $name

In this example, the initial input value will be "Gil Bates" (including the quotes).

To work around this problem, you can use the following PowerShell function:

function Read-Input {
  param(
    [String] $Prompt,
    [String] $InitialValue,
    [Switch] $Hidden
  )
  $arguments = '-p "{0}" ' -f $Prompt
  if ( $Hidden ) { $arguments += " -h " }
  $arguments += " -- {0}" -f $InitialValue
  $process = New-Object System.Diagnostics.Process
  $process.StartInfo.FileName = (get-command "ReadLine.exe").Definition
  $process.StartInfo.Arguments = $arguments
  $process.StartInfo.UseShellExecute = $false
  $process.StartInfo.RedirectStandardOutput = $true
  if ( $process.Start() ) {
    $process.StandardOutput.ReadToEnd() -replace "\r\n$",""
  }
}

This function assumes that ReadLine.exe is in a directory in the Path. If it isn't in the Path, you will need to set the FileName property of the $process.StartInfo object accordingly; for example:

$process.StartInfo.FileName = "C:\Utilities\ReadLine.exe"

Once you define the Read-Input function, you can use code like the following:

$name = "Gil Bates"
$name = Read-Input -Prompt "Enter name: " -InitialValue $name
"You entered: $name"

You can also add the -Hidden parameter to the function, as follows:

$pass = Read-Input -Prompt "Enter password: " -Hidden
"You entered: $pass"

The -Hidden parameter has limited utility in PowerShell because PowerShell already has Read-Host -AsSecureString to retrieve hidden input.

If you decide to use Read-Host -AsSecureString, note that it returns a SecureString object, not a String object. If need to convert the SecureString object to plain text, here's a PowerShell function that does this:

function ConvertTo-String {
  param(
    [System.Security.SecureString] $secureString
  )
  try {
    $intPtr = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($secureString)
    $string = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($intPtr)
  }
  finally {
    if ( $intPtr ) {
      [System.Runtime.InteropServices.Marshal]::ZeroFreeBSTR($intPtr)
    }
  }
  $string
}

Here's an example of how you could use the ConvertTo-String function:

$pass = ConvertTo-String (Read-Host "Enter password" -AsSecureString)
"You entered: $pass"

Sample Scripts

EditPath.cmd

EditPath.cmd is a cmd.exe shell script (batch file) that lets you interactively edit the Path environment variable.

@echo off
setlocal enableextensions enabledelayedexpansion
for /f "delims=" %%p in ('readline -p "PATH=" -- !PATH!') do set _PATH=%%p
if {!_PATH!}=={} (
  echo Empty path not permitted
  endlocal
  goto :EOF
)
endlocal & set PATH=%_PATH%

EditPath.ps1

EditPath.ps1 is a PowerShell script that lets you interactively edit the Path environment variable. It uses the Read-Input function discussed previously. Note that if you run this script in the ISE, the input string will appear in a separate console window. There is no workaround for this because EditLine.exe is a console program.

function Read-Input {
  param(
    [String] $Prompt,
    [String] $InitialValue,
    [Switch] $Hidden
  )
  $arguments = '-p "{0}" ' -f $Prompt
  if ( $Hidden ) { $arguments += " -h " }
  $arguments += " -- {0}" -f $InitialValue
  $process = New-Object System.Diagnostics.Process
  $process.StartInfo.FileName = (get-command "ReadLine.exe").Definition
  $process.StartInfo.Arguments = $arguments
  $process.StartInfo.UseShellExecute = $false
  $process.StartInfo.RedirectStandardOutput = $true
  if ( $process.Start() ) {
    $process.StandardOutput.ReadToEnd() -replace "\r\n$",""
  }
}
$path = Read-Input -Prompt "PATH=" -InitialValue $Env:Path
if ( $path -eq "" ) {
  throw "Empty path not permitted"
}
else {
  $Env:Path = $path
}

Limitations