命令行解析算法

能够对复杂的引号嵌套、空格进行解析。

如:cmd -a "   a   b  \"  '   '   \" c  " -b abc   =>   cmd  -a     a   b  "  '   '   " c     -b  abc

func CmdToArgs(cmd string) []string {
	var args []string
	var closedChar byte      // 闭合字符 ' or "
	var needClose bool       // 当前是否需要闭合
	var toCompleteArg string // 要拼接的完成的参数,就是 ' 或 " 包围的参数值

	addToArgs := func() {
		toCompleteArg = strings.ReplaceAll(toCompleteArg, `\'`, `'`)
		toCompleteArg = strings.ReplaceAll(toCompleteArg, `\"`, `"`)
		args = append(args, toCompleteArg)
		toCompleteArg = ""
	}

	for i := 0; i < len(cmd); i++ {
		if (cmd[i] == '\'' || cmd[i] == '"') && cmd[i-1] != '\\' { // 不用判断 i > 0
			if !needClose {
				needClose = true
				closedChar = cmd[i]
				continue
			} else if cmd[i] == closedChar {
				needClose = false
				addToArgs()
				continue
			}
		}
		if cmd[i] == ' ' && needClose { // 空格作为参数值的一部分
			toCompleteArg += " "
		} else if cmd[i] == ' ' && toCompleteArg != "" { // 非引号闭合的参数结束
			addToArgs()
		} else if cmd[i] != ' ' {
			toCompleteArg += string(cmd[i])
			if i == len(cmd)-1 {
				addToArgs()
			}
		}
	}
	return args
}

  

测试用例:

func TestCmdToArgs(t *testing.T) {
    testCases := []struct {
        input string
        want []string
    }{
        {`cmd    -i "1    ' \" 2"`, []string{`cmd`, `-i`, `1    ' " 2`}},
        {` cmd -id 123 -name '' -e "  \'\'\"  "`, []string{`cmd`, `-id`, `123`, `-name`, ``, `-e`, `  ''"  `}},
        {`cmd -a ' "  " ' -b " ''aa" -c cc`, []string{`cmd`, `-a`, ` "  " `, `-b`, ` ''aa`, `-c`, `cc`}},
    }

    sliceToStr := func(s []string) (str string) {
        for _, i := range s {
            str += i
        }
        return str
    }

    for _, testCase := range testCases {
        if sliceToStr(cmdToArgs(testCase.input)) != sliceToStr(testCase.want) {
            t.Errorf("input:%s   want:%s", testCase.input, testCase.want)
        }
    }
}

  

posted @ 2020-10-28 10:52  yuanyb  阅读(325)  评论(0编辑  收藏  举报