2

Good Afternoon,

I think I am a little over my head in this particular task. I am trying to create a regex match function to input a command, and split up the command name, parameters, and parameter values.

New-Variable -Name Something -Force Results should be

  1. New-Variable
  2. -Name
  3. Something
  4. -Force

I have come up with this so far, but it only captures the 1st argument set.

Bonus: Is there any way to make all the matches after the command incrementally named? Say Parameter1, Value1, Parameter2, Value2, etc?

^(?P<Command>[a-zA-Z]+-[a-zA-Z]+)(?: +)((-\S+)(?: |:|=)(.*){0,1})(?: +)

I had no idea the PowerShell parser even existed but this is awesome. This is the code I settled on. Thank you guys for the help!

#Split up the command argument, needed to pull useful information from the command.
New-Variable -force -Name SplitCommand -Value ([System.array]$Null)
$null = [System.Management.Automation.Language.Parser]::ParseInput($Command, [ref]$SplitCommand,[ref]$Null)
$SplitCommand = $SplitCommand.where({-NOT [String]::IsNullOrEmpty($_.text)}).text

2 Answers 2

5

Don't use regex for this - use the builtin parser instead:

# Prepare command to parse
$command = 'New-Variable -Name Something -Force'

# Parse command invocation - Parser will return an Abstract Syntax Tree object
$parserErrors = @()
$AST = [System.Management.Automation.Language.Parser]::ParseInput($command, [ref]$null, [ref]$parserErrors)

if($parserErrors){
    # error encountered while parsing script
}
else {
    # No errors, let's search the AST for the first command invocation
    $CommandInvocation = $AST.Find({ $args[0] -is [System.Management.Automation.Language.CommandAst] }, $false)

    # Get the string representation of each syntax element in the command invocation
    $elements = $CommandInvocation.CommandElements |ForEach-Object ToString
}

Which, with your input (New-Variable -Name Something -Force), produces the following strings:

PS ~> $elements
New-Variable
-Name
Something
-Force

NOTE: arguments tightly bound to a parameter with : will be interpreted as a single conjoined syntax element, eg. 'Get-Thing -Name:nameOfThing' will produce only two strings (Get-Thing and -Name:nameOfThing) - if you want them split into separate strings, take that into account before converting them to strings:

$elements = $CommandInvocation.CommandElements |ForEach-Object {
  if($null -ne $_.Argument){
    # tightly bound argument, output both separately
    "-$($_.ParameterName)"
    $_.Argument
  } else {
    # just the parameter name, output as-is
    $_
  }
} |ForEach-Object ToString
Sign up to request clarification or add additional context in comments.

5 Comments

it never ceases to amaze me how much I still don't know about powershell despite using it for years and writing code in it fairly frequently. Ty.
@NickW. The rabbit hole is deep ^_^ You're welcome!
Just did some testing and I don't think the second part is needed, it seems to process ":" just fine without it.
@NickW. It's not needed if you want -param:arg represented as a single string - but if you want param names and arguments strictly separated, it is :-)
what I mean is when I pass a -Parameter:value it does separate them.
2

To complement Mathias R. Jessen's helpful answer with a streamlined solution, assuming:

  • you're simply interest in an array of strings representing the command name and its arguments.

  • that the string represents a syntactically valid command, i.e. that you needn't worry about error handling.

$stringToParse = 'New-Variable -Name Something -Force'

$tokens = $null # Initialize the output variable.
# Parse the string as a PowerShell command.
$null = [System.Management.Automation.Language.Parser]::ParseInput(
  $stringToParse, 
  [ref] $tokens,  # will receive a collection of tokens
  [ref] $null     # would receive a collection of parsing errors; unused here
)

$tokenStrings = $tokens.Text -ne '' # -ne '' ignores the end-of-input token

$tokenStrings then contains an array of strings with verbatim elements

New-Variable, -Name, Something, -Force.

See also:

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.