用空格分割shell命令,但允许多字参数用引号引起来

时间:2019-01-11 15:36:41

标签: vb.net

我想编写一个像命令外壳一样工作的应用程序(询问命令,执行它,然后询问下一个命令,重复)。但是,当我尝试将书面命令分成几个单独的部分时,我立即失败了。

我不仅要在空格处分割命令,而且还希望允许参数包含空格,只要将它们放在双引号之间即可。

即使我逐行检查代码,也无法弄清楚代码出了什么问题。

Module Module1
    Dim _exit As Boolean = False
    Dim prompt As String = ""
    Dim Title As String = ""

    Sub Main()
        Console.Title = Title
        While Not _exit
            Console.Write(prompt)
            Dim returned As String = Console.ReadLine
            Dim parts() As String = split_with_string(returned, False)
            If parts(0) = "exit" Then
                _exit = True
            ElseIf parts(0) = "" Then
                'Space for Commands
            Else
                Console.WriteLine("Unknown Command")
            End If
        End While
    End Sub

    Private Function secindex(ByVal str As String, character As Char)
        Return str.IndexOf(character, str.IndexOf(character) + 1)
    End Function

    Private Function split_with_string(ByVal str As String, ByVal return_parts As Boolean)
        Dim returned As String = str
        Dim parts As New List(Of String)
_next:
        If returned.Length = 1 Then
            parts.Add(returned)
        ElseIf returned.StartsWith("""") Then
            parts.Add(returned.Substring(1, secindex(returned, """") - 1))
            returned = returned.Substring(secindex(returned, """") + 1)
        ElseIf returned.StartsWith(" ") Then
            returned = returned.Substring(1)
        Else
            If returned.Contains(" ") Then
                parts.Add(returned.Substring(0, returned.IndexOf(" ")))
                returned = returned.Substring(returned.IndexOf(" ") + 1)
            Else
                parts.Add(returned)
                returned = ""
            End If
        End If
        If Not returned.Length < 1 Then
            GoTo _next
        End If
        If return_parts Then
            For Each xx In parts.ToArray
                Console.WriteLine(xx)
            Next
        End If
        Return parts.ToArray
    End Function
End Module

如果布尔值设置为true,我希望函数将结果写入控制台。

1 个答案:

答案 0 :(得分:3)

Regex使复杂的字符串解析变得如此容易。它具有自己的陡峭的学习曲线,但是由于它是许多语言和编程工具中都可以使用的标准工具,所以您迟早要熟悉它。

例如,如果您要获取如下所示的列表:

one
two
"this is three"
"this is four"
five

您可以使用如下代码:

Public Sub Main()
    Dim parts() As String = ParseCommand("one two ""this is three"" ""this is four"" five")
    For Each part As String In parts
        Console.WriteLine(part)
    Next
End Sub

Private Function ParseCommand(input As String) As String()
    Return Regex.Matches(input, "(?!"")\S+|""[^""]+""").
        Cast(Of Match)().
        Select(Function(m) m.Value).
        ToArray()
End Function

但是,如果您希望它去除引号,那么输出如下所示:

one
two
this is three
this is four
five

您可以对正则表达式模式进行一些调整,例如:

Private Function ParseCommand(input As String) As String()
    Return Regex.Matches(input, "(?<p>(?!"")\S+)|""(?<p>[^""]+)""").
        Cast(Of Match)().
        Select(Function(m) m.Groups("p").Value).
        ToArray()
End Function

如果您对正则表达式不熟悉,它会使用一种模式在输入字符串中寻找匹配的子字符串。在第一个示例中,它要寻找的模式是(?!")\S+|"[^"]+"。因此,它将在输入字符串中查找与该模式匹配的所有部分,并仅返回那些匹配的子字符串。模式的含义如下:

  • (?!")\S+-与任何以"开头的单词匹配
    • (?!")-(?! ... )子句中包含的所有内容均称为否定预见。这意味着紧随其后的是该子句中列出的所有内容。在这种情况下,这意味着接下来出现的所有内容都不能以"字符开头。
    • \S-这表示任何非空格字符,因此任何非空格或制表符之类的东西
    • +-这意味着必须有一个或多个这些非空格字符
  • |-这是布尔值OR,表示它可以匹配之前或之后的所有内容
  • "[^"]+"-这匹配两个"字符之间的所有单词,包括任何空格
    • "-说匹配项必须以"开头
    • [^"]-[^ ... ]子句中包含的所有内容均为负字符类。这意味着它可以是不是该子句中列出的字符之一的任何字符。在这种情况下,就是说任何不是"的字符。
    • +-这意味着必须有一个或多个这些非引号字符
    • "-说匹配的内容必须以"
    • 结尾

第二种模式基本上是同一回事,但是它稍微先进一点,因为它使用命名组(例如(?<name> sub-pattern))来匹配整个匹配的某些部分。