batch-filecmdcommand-prompt

Can I mask an input text in a bat file?


I am writing a batch file to execute some other programs. In this case I need to prompt for a password. Do I have any way to mask the input text? I don't need to print ******* characters instead of input characters. Linux's Password prompt behavior (Print nothing while typing) is enough.

@echo off
SET /P variable=Password : 
echo %variable%
Pause

This will read the input but I can't mask the text using this approach.


Solution

  • Up to XP and Server 2003, you can make use of another included tool (VBScript) - the following two scripts do the job you want.

    First, getpwd.cmd:

    @echo off
    <nul: set /p passwd=Password: 
    for /f "delims=" %%i in ('cscript /nologo getpwd.vbs') do set passwd=%%i
    echo.
    

    Then, getpwd.vbs:

    Set oScriptPW = CreateObject("ScriptPW.Password")
    strPassword = oScriptPW.GetPassword()
    Wscript.StdOut.WriteLine strPassword
    

    The getpwd.vbs simply uses the password object to input the password from the user and then print it to standard output (the next paragraph will explain why that doesn't show up in the terminal).

    The getpwd.cmd command script is a bit trickier but it basically works as follows.

    The effect of the "<nul: set /p passwd=Password: " command is to output the prompt with no trailing newline character - it's a sneaky way to emulate the "echo -n" command from the bash shell. It sets passwd to an empty string as an irrelevant side effect and doesn't wait for input since it's taking its input from the nul: device.

    The "for /f "delims=" %%i in ('cscript /nologo getpwd.vbs') do set passwd=%%i" statement is the trickiest bit. It runs the VBScript with no Microsoft "advertising", so that the only line output is the password (from the VBscript "Wscript.StdOut.WriteLine strPassword".

    Setting the delimiters to nothing is required to capture an entire input line with spaces, otherwise you just get the first word. The "for ... do set ..." bit sets passwd to be the actual password output from the VBScript.

    Then we echo a blank line (to terminate the "Password: " line) and the password will be in the passwd environment variable after the code has run.


    Now, as mentioned, scriptpw.dll is available only up to XP/2003. In order to rectify this, you can simply copy the scriptpw.dll file from the Windows\System32 folder of an XP/2003 system to the Winnt\System32 or Windows\System32 folder on your own system. Once the DLL has been copied, you will need to register it by running:

    regsvr32 scriptpw.dll
    

    To successfully register the DLL on Vista and later, you will need administrator privileges. I haven't examined the legality of such a move so cave lector.


    If you're not overly keen on trying to track down and register older DLL files (for convenience or legal reasons), there is another way. Later versions of Windows (the ones that don't have the required DLL) should have Powershell available to you.

    And, in fact, you really should consider upgrading your scripts to use it fully since it's a much more capable scripting language than cmd.exe. However, if you want to keep the bulk of your code as cmd.exe scripts (such as if you have a lot of code that you don't want to convert), you can use the same trick.

    First, modify the cmd script so it calls Powershell rather than CScript:

    @echo off
    for /f "delims=" %%i in ('powershell -file getpwd.ps1') do set passwd=%%i
    

    The Powershell script is equally simple:

    $password = Read-Host "Enter password" -AsSecureString
    $password = [Runtime.InteropServices.Marshal]::SecureStringToBSTR($password)
    $password = [Runtime.InteropServices.Marshal]::PtrToStringAuto($password)
    echo $password
    

    although with some marshalling to get the actual password text.

    Remember that, to run local unsigned Powershell scripts on your machine, you may need to modify the execution policy from the (draconian, though very safe) default, with something like:

    set-executionpolicy remotesigned
    

    from within Powershell itself.