What should I do? (exclamation mark) when using Read-Host?

Let's start with normal behavior. When I run Read-Host without a hint, I can enter a line starting with an exclamation point:

 PS C:\> Read-Host | Foreach-Object { Write-Host 'Entered' $_ } !hi, mom Entered !hi, mom 

(Note that I'm just connecting to the Foreach-Object as an easy way to prefix the output. The behavior of interest is the same if you run the Read-Host without it.)

But if I give the Read-Host prompt argument, the behavior is completely different:

 PS C:\> Read-Host 'Enter something' | Foreach-Object { Write-Host 'Entered' $_ } Enter something: !hi, mom "!hi, mom" cannot be recognized as a valid Prompt command. Enter something: !!hi, mom Entered !hi, mom 

Exclamation seems to allow me to do something other than a simple string type. PowerShell interprets the exclamation, meaning that I enter some kind of command to run it, but I can not find the documentation about what is allowed. Besides doubling the exclamation to avoid it, I cannot figure out what is a valid command.

Please note that input must begin with an exclamation point. Terminating with it does not cause this behavior:

 PS C:\> Read-Host 'Enter something' | Foreach-Object { Write-Host 'Entered' $_ } Enter something: hi, mom! Entered hi, mom! 

So what can I do with ! here? What is a valid team, except that it simply eludes exclamation? The work around is helpful, but I'm really curious if I can execute code or something like that.

I use PowerShell 4, but that seems a date back a lot earlier.

+5
source share
3 answers

Using JetBrains dotPeek, I found an implementation for where '!' being processed. It is located in Microsoft.PowerShell.ConsoleHostUserInterface.PromptCommandMode in the Microsoft.Powershell.ConsoleHost assembly. This is PS 3.0. Below is the parsed code.

Checking strA.StartsWith should be to check if there is a '!' escapes with another "!".

Note that the check for strA[0] == 63 is a check for '?' (0x3F). Any other single char input gives an error message in the OP. Two double quotes give an empty string (as Bruce Payet says in the link referenced in the OP comments), and the string '$null' gives $null .

Everything else gives the same error message. So, with the exception of any proxying or spelling of your Host, "!" cannot be used for other teams.

 private string PromptCommandMode(string input, FieldDescription desc, out bool inputDone) { string strA = input.Substring(1); inputDone = true; if (strA.StartsWith("!", StringComparison.OrdinalIgnoreCase)) return strA; if (strA.Length == 1) { if ((int) strA[0] == 63) { if (string.IsNullOrEmpty(desc.HelpMessage)) { string str = StringUtil.Format(ConsoleHostUserInterfaceStrings.PromptNoHelpAvailableErrorTemplate, (object) desc.Name); ConsoleHostUserInterface.tracer.TraceWarning(str); this.WriteLineToConsole(this.WrapToCurrentWindowWidth(str)); } else this.WriteLineToConsole(this.WrapToCurrentWindowWidth(desc.HelpMessage)); } else this.ReportUnrecognizedPromptCommand(input); inputDone = false; return (string) null; } if (strA.Length == 2 && string.Compare(strA, "\"\"", StringComparison.OrdinalIgnoreCase) == 0) return string.Empty; if (string.Compare(strA, "$null", StringComparison.OrdinalIgnoreCase) == 0) return (string) null; this.ReportUnrecognizedPromptCommand(input); inputDone = false; return (string) null; } private void ReportUnrecognizedPromptCommand(string command) { this.WriteLineToConsole(this.WrapToCurrentWindowWidth(StringUtil.Format(ConsoleHostUserInterfaceStrings.PromptUnrecognizedCommandErrorTemplate, (object) command))); } 
+3
source

TL; DR

  • Read-Host , as the name implies, is host-specific.

    • As in PSv5.1, the standard console host (console windows) and in PSv3 +, ISE also exhibit unexpected behavior. (In PSv2-ISE, it used its own Read-Host GUI prompt, which was not affected).
  • The behavior in action is an unrelated function that seems to be randomly exposed , namely , only if the Read-Host string parameter of the query string string ( -Prompt ) is -Prompt .

    • Given that the behavior is undocumented and shown in this obscure manner , it should be considered a mistake .
  • Work on the error as follows:

    • Write-Host -NoNewline 'Enter something: '; Read-Host
    • Ie: Use Write-Host to print the prompt line separately, previously (without line break), then call Read-Host without the -Prompt parameter:

To answer the title of the question, โ€œWhat should I do with Read-Host?โ€: Nothing useful.
To find out why and to get some background information, read on.


The behavior - and complaints about it - refers to the very first version of PowerShell; in the column also associated with the OP , an MS employee calls it โ€œShortcutsโ€ , and one of its architects explains this in more detail here by @TheMadTechnician in a comment on the question .
Note that this is discussed in the context of how PowerShell itself requests the absence of required parameter values that really should not be related to Read-Host :

When requesting a required parameter value that is not specified on the command line (and unexpectedly also when using Read-Host with a -Prompt value) ! , since the very first character (only) starts the "prompt command":

  • !? calls the help line for (description) of this parameter.

    • In the context of Read-Host the prompt string (!) Is interpreted as the name of the parameter whose search is requested, and therefore no help is found ( No help is available for <prompt-string> ).
  • !"" allows you to enter an empty string as a parameter value for an array parameter, allowing you to enter additional values โ€‹โ€‹(just pressing Enter will immediately end the prompt).

    • In the context of Read-Host this simply displays an empty string.
  • !! allows you to enter a literal !

  • An excellent answer uses dismantling the base builds to show that, at least with PS v3, no additional commands are supported.

The linked forum post completes (emphasis mine):

Note that this is an artifact of the current implementation of the PowerShell host request . This is not part of the engine core. The GUI host is unlikely to use this notation . A reasonable enhancement will be to allow the prompt command to be configured by the user .

After 10 years, the behavior - at least in the context of Read-Host - is not documented or configured.

Before the message was discovered, jpmc26 itself discovered that the behavior was related to how PowerShell itself requested the absence of required arguments; eg:.

 # Define a test function with a mandatory parameter. > function foo([Parameter(Mandatory=$true,HelpMessage='fooParam help')][string]$fooParam) {} # Invoke the test function *without* that mandatory parameter, # which causes Powershell to *prompt* for it. > foo cmdlet foo at command pipeline position 1 Supply values for the following parameters: (Type !? for Help.) fooParam: !? # !? asks for help on the parameter, which prints the HelpMessage attribute. fooParam help fooParam: # After having printed parameter help, the prompt is shown again. 
+6
source

Here is another piece of the puzzle.
I run V5 and it works in ISE:

 PS C:\> $PSVersionTable Name Value ---- ----- PSVersion 5.0.10586.51 PSCompatibleVersions {1.0, 2.0, 3.0, 4.0...} BuildVersion 10.0.10586.51 CLRVersion 4.0.30319.34209 WSManStackVersion 3.0 PSRemotingProtocolVersion 2.3 SerializationVersion 1.1.0.1 PS C:\> Read-Host 'Enter something' | Foreach-Object { Write-Host 'Entered' $_} Enter something: !Hi, mom Entered !Hi, mom 

But this does not work from a regular command line:

 PS C:\> Read-Host 'Enter something' | Foreach-Object { Write-Host "Entered $_"} Enter something: !Hi, mom "!Hi, mom" cannot be recognized as a valid Prompt command. Enter something: 
+2
source

Source: https://habr.com/ru/post/1243348/


All Articles