github.com/fumiama/NanoBot@v0.0.0-20231122134259-c22d8183efca/shell.go (about) 1 package nano 2 3 import ( 4 "flag" 5 "reflect" 6 "strings" 7 ) 8 9 func isSpace(r rune) bool { 10 switch r { 11 case ' ', '\t', '\r', '\n': 12 return true 13 } 14 return false 15 } 16 17 type argType int 18 19 const ( 20 argNo argType = iota 21 argSingle 22 argQuoted 23 ) 24 25 // ParseShell 将指令转换为指令参数. 26 // modified from https://github.com/mattn/go-shellwords 27 func ParseShell(s string) []string { 28 var args []string 29 buf := strings.Builder{} 30 var escaped, doubleQuoted, singleQuoted, backQuote bool 31 backtick := "" 32 33 got := argNo 34 35 for _, r := range s { 36 if escaped { 37 buf.WriteRune(r) 38 escaped = false 39 got = argSingle 40 continue 41 } 42 43 if r == '\\' { 44 if singleQuoted { 45 buf.WriteRune(r) 46 } else { 47 escaped = true 48 } 49 continue 50 } 51 52 if isSpace(r) { 53 if singleQuoted || doubleQuoted || backQuote { 54 buf.WriteRune(r) 55 backtick += string(r) 56 } else if got != argNo { 57 args = append(args, buf.String()) 58 buf.Reset() 59 got = argNo 60 } 61 continue 62 } 63 64 switch r { 65 case '`': 66 if !singleQuoted && !doubleQuoted { 67 backtick = "" 68 backQuote = !backQuote 69 } 70 case '"': 71 if !singleQuoted { 72 if doubleQuoted { 73 got = argQuoted 74 } 75 doubleQuoted = !doubleQuoted 76 } 77 case '\'': 78 if !doubleQuoted { 79 if singleQuoted { 80 got = argSingle 81 } 82 singleQuoted = !singleQuoted 83 } 84 default: 85 got = argSingle 86 buf.WriteRune(r) 87 if backQuote { 88 backtick += string(r) 89 } 90 } 91 } 92 93 if got != argNo { 94 args = append(args, buf.String()) 95 } 96 97 return args 98 } 99 100 var ( 101 boolType = reflect.TypeOf(false) 102 intType = reflect.TypeOf(0) 103 stringType = reflect.TypeOf("") 104 float64Type = reflect.TypeOf(float64(0)) 105 ) 106 107 func registerFlag(t reflect.Type, v reflect.Value) *flag.FlagSet { 108 v = v.Elem() 109 fs := flag.NewFlagSet("", flag.ContinueOnError) 110 for i := 0; i < t.NumField(); i++ { 111 field := t.Field(i) 112 name := field.Tag.Get("flag") 113 if name == "" { 114 continue 115 } 116 help := field.Tag.Get("help") 117 switch field.Type { 118 case boolType: 119 fs.BoolVar(v.Field(i).Addr().Interface().(*bool), name, false, help) 120 case intType: 121 fs.IntVar(v.Field(i).Addr().Interface().(*int), name, 0, help) 122 case stringType: 123 fs.StringVar(v.Field(i).Addr().Interface().(*string), name, "", help) 124 case float64Type: 125 fs.Float64Var(v.Field(i).Addr().Interface().(*float64), name, 0, help) 126 default: 127 panic("unsupported type") 128 } 129 } 130 return fs 131 }