Nick W.
Nick W.

Reputation: 1614

Regex to take command and split up Command, Arguments, and Argument Values

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

Upvotes: 2

Views: 411

Answers (2)

Mathias R. Jessen
Mathias R. Jessen

Reputation: 174445

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

Upvotes: 5

mklement0
mklement0

Reputation: 437111

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:

Upvotes: 2

Related Questions