Merge branch 'master' of https://github.com/seaweedfs/seaweedfs
This commit is contained in:
@@ -7,7 +7,6 @@ import (
|
||||
"math/rand/v2"
|
||||
"os"
|
||||
"path"
|
||||
"regexp"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
@@ -43,8 +42,6 @@ func RunShell(options ShellOptions) {
|
||||
|
||||
defer saveHistory()
|
||||
|
||||
reg, _ := regexp.Compile(`'.*?'|".*?"|\S+`)
|
||||
|
||||
commandEnv := NewCommandEnv(&options)
|
||||
|
||||
ctx := context.Background()
|
||||
@@ -89,25 +86,21 @@ func RunShell(options ShellOptions) {
|
||||
}
|
||||
|
||||
for _, c := range util.StringSplit(cmd, ";") {
|
||||
if processEachCmd(reg, c, commandEnv) {
|
||||
if processEachCmd(c, commandEnv) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func processEachCmd(reg *regexp.Regexp, cmd string, commandEnv *CommandEnv) bool {
|
||||
cmds := reg.FindAllString(cmd, -1)
|
||||
func processEachCmd(cmd string, commandEnv *CommandEnv) bool {
|
||||
cmds := splitCommandLine(cmd)
|
||||
|
||||
if len(cmds) == 0 {
|
||||
return false
|
||||
} else {
|
||||
|
||||
args := make([]string, len(cmds[1:]))
|
||||
|
||||
for i := range args {
|
||||
args[i] = strings.Trim(string(cmds[1+i]), "\"'")
|
||||
}
|
||||
args := cmds[1:]
|
||||
|
||||
cmd := cmds[0]
|
||||
if cmd == "help" || cmd == "?" {
|
||||
@@ -133,6 +126,70 @@ func processEachCmd(reg *regexp.Regexp, cmd string, commandEnv *CommandEnv) bool
|
||||
return false
|
||||
}
|
||||
|
||||
func stripQuotes(s string) string {
|
||||
tokens, unbalanced := parseShellInput(s, false)
|
||||
if unbalanced {
|
||||
return s
|
||||
}
|
||||
if len(tokens) > 0 {
|
||||
return tokens[0]
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func splitCommandLine(line string) []string {
|
||||
tokens, _ := parseShellInput(line, true)
|
||||
return tokens
|
||||
}
|
||||
|
||||
func parseShellInput(line string, split bool) (args []string, unbalanced bool) {
|
||||
var current strings.Builder
|
||||
inDoubleQuotes := false
|
||||
inSingleQuotes := false
|
||||
escaped := false
|
||||
|
||||
for i := 0; i < len(line); i++ {
|
||||
c := line[i]
|
||||
|
||||
if escaped {
|
||||
current.WriteByte(c)
|
||||
escaped = false
|
||||
continue
|
||||
}
|
||||
|
||||
if c == '\\' && !inSingleQuotes {
|
||||
escaped = true
|
||||
continue
|
||||
}
|
||||
|
||||
if c == '"' && !inSingleQuotes {
|
||||
inDoubleQuotes = !inDoubleQuotes
|
||||
continue
|
||||
}
|
||||
|
||||
if c == '\'' && !inDoubleQuotes {
|
||||
inSingleQuotes = !inSingleQuotes
|
||||
continue
|
||||
}
|
||||
|
||||
if split && (c == ' ' || c == '\t' || c == '\n' || c == '\r') && !inDoubleQuotes && !inSingleQuotes {
|
||||
if current.Len() > 0 {
|
||||
args = append(args, current.String())
|
||||
current.Reset()
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
current.WriteByte(c)
|
||||
}
|
||||
|
||||
if current.Len() > 0 {
|
||||
args = append(args, current.String())
|
||||
}
|
||||
|
||||
return args, inDoubleQuotes || inSingleQuotes || escaped
|
||||
}
|
||||
|
||||
func printGenericHelp() {
|
||||
msg :=
|
||||
`Type: "help <command>" for help on <command>. Most commands support "<command> -h" also for options.
|
||||
|
||||
105
weed/shell/shell_liner_test.go
Normal file
105
weed/shell/shell_liner_test.go
Normal file
@@ -0,0 +1,105 @@
|
||||
package shell
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSplitCommandLine(t *testing.T) {
|
||||
tests := []struct {
|
||||
input string
|
||||
expected []string
|
||||
}{
|
||||
{
|
||||
input: `s3.configure -user=test`,
|
||||
expected: []string{`s3.configure`, `-user=test`},
|
||||
},
|
||||
{
|
||||
input: `s3.configure -user=Test_number_004 -account_display_name="Test number 004" -actions=write -apply`,
|
||||
expected: []string{`s3.configure`, `-user=Test_number_004`, `-account_display_name=Test number 004`, `-actions=write`, `-apply`},
|
||||
},
|
||||
{
|
||||
input: `s3.configure -user=Test_number_004 -account_display_name='Test number 004' -actions=write -apply`,
|
||||
expected: []string{`s3.configure`, `-user=Test_number_004`, `-account_display_name=Test number 004`, `-actions=write`, `-apply`},
|
||||
},
|
||||
{
|
||||
input: `s3.configure -flag="a b"c'd e'`,
|
||||
expected: []string{`s3.configure`, `-flag=a bcd e`},
|
||||
},
|
||||
{
|
||||
input: `s3.configure -name="a\"b"`,
|
||||
expected: []string{`s3.configure`, `-name=a"b`},
|
||||
},
|
||||
{
|
||||
input: `s3.configure -path='a\ b'`,
|
||||
expected: []string{`s3.configure`, `-path=a\ b`},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
got := splitCommandLine(tt.input)
|
||||
if !reflect.DeepEqual(got, tt.expected) {
|
||||
t.Errorf("input: %s\ngot: %v\nwant: %v", tt.input, got, tt.expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestStripQuotes(t *testing.T) {
|
||||
tests := []struct {
|
||||
input string
|
||||
expected string
|
||||
}{
|
||||
{input: `"Test number 004"`, expected: `Test number 004`},
|
||||
{input: `'Test number 004'`, expected: `Test number 004`},
|
||||
{input: `-account_display_name="Test number 004"`, expected: `-account_display_name=Test number 004`},
|
||||
{input: `-flag="a"b'c'`, expected: `-flag=abc`},
|
||||
{input: `-name="a\"b"`, expected: `-name=a"b`},
|
||||
{input: `-path='a\ b'`, expected: `-path=a\ b`},
|
||||
{input: `"unbalanced`, expected: `"unbalanced`},
|
||||
{input: `'unbalanced`, expected: `'unbalanced`},
|
||||
{input: `-name="a\"b`, expected: `-name="a\"b`},
|
||||
{input: `trailing\`, expected: `trailing\`},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
got := stripQuotes(tt.input)
|
||||
if got != tt.expected {
|
||||
t.Errorf("input: %s, got: %s, want: %s", tt.input, got, tt.expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFlagParsing(t *testing.T) {
|
||||
fs := flag.NewFlagSet("test", flag.ContinueOnError)
|
||||
displayName := fs.String("account_display_name", "", "display name")
|
||||
|
||||
rawArg := `-account_display_name="Test number 004"`
|
||||
args := []string{stripQuotes(rawArg)}
|
||||
err := fs.Parse(args)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
expected := "Test number 004"
|
||||
if *displayName != expected {
|
||||
t.Errorf("got: [%s], want: [%s]", *displayName, expected)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEscapedFlagParsing(t *testing.T) {
|
||||
fs := flag.NewFlagSet("test", flag.ContinueOnError)
|
||||
name := fs.String("name", "", "name")
|
||||
|
||||
rawArg := `-name="a\"b"`
|
||||
args := []string{stripQuotes(rawArg)}
|
||||
err := fs.Parse(args)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
expected := `a"b`
|
||||
if *name != expected {
|
||||
t.Errorf("got: [%s], want: [%s]", *name, expected)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user