github.com/lmorg/murex@v0.0.0-20240217211045-e081c89cd4ef/lang/expressions/parse_subshell.go (about) 1 package expressions 2 3 import ( 4 "fmt" 5 6 "github.com/lmorg/murex/lang" 7 "github.com/lmorg/murex/lang/expressions/primitives" 8 "github.com/lmorg/murex/lang/types" 9 "github.com/lmorg/murex/utils" 10 "github.com/lmorg/murex/utils/consts" 11 ) 12 13 func (tree *ParserT) parseSubShell(exec bool, sigil rune, strOrVal varFormatting) ([]rune, primitives.FunctionT, error) { 14 start := tree.charPos 15 16 tree.charPos += 2 17 18 _, err := tree.parseBlockQuote() 19 if err != nil { 20 return nil, nil, err 21 } 22 23 value := tree.expression[start : tree.charPos+1] 24 block := tree.expression[start+2 : tree.charPos] 25 26 if !exec { 27 return value, nil, nil 28 } 29 30 switch sigil { 31 case '$': 32 fn := func() (*primitives.Value, error) { 33 return execSubShellScalar(tree, block, strOrVal) 34 } 35 return value, fn, nil 36 37 case '@': 38 fn := func() (*primitives.Value, error) { 39 return execSubShellArray(tree, block, strOrVal) 40 } 41 return value, fn, nil 42 default: 43 err = fmt.Errorf("invalid prefix in expression '%s'. %s", string(sigil), consts.IssueTrackerURL) 44 return nil, nil, err 45 } 46 } 47 48 func execSubShellScalar(tree *ParserT, block []rune, strOrVal varFormatting) (*primitives.Value, error) { 49 if tree.p == nil { 50 panic("`tree.p` is undefined") 51 } 52 53 val := new(primitives.Value) 54 var err error 55 56 fork := tree.p.Fork(lang.F_NO_STDIN | lang.F_PARENT_VARTABLE | lang.F_CREATE_STDOUT) 57 val.ExitNum, err = fork.Execute(block) 58 if err != nil { 59 return val, fmt.Errorf("subshell failed: %s", err.Error()) 60 } 61 if val.ExitNum > 0 && tree.p.RunMode.IsStrict() { 62 return val, fmt.Errorf("subshell exit status %d", val.ExitNum) 63 } 64 b, err := fork.Stdout.ReadAll() 65 if err != nil { 66 return val, fmt.Errorf("cannot read from subshell's STDOUT: %s", err.Error()) 67 } 68 69 b = utils.CrLfTrim(b) 70 val.DataType = fork.Stdout.GetDataType() 71 72 val.Value, err = formatBytes(b, val.DataType, strOrVal) 73 return val, err 74 } 75 76 func execSubShellArray(tree *ParserT, block []rune, strOrVal varFormatting) (*primitives.Value, error) { 77 var slice []interface{} 78 val := new(primitives.Value) 79 var err error 80 81 fork := tree.p.Fork(lang.F_NO_STDIN | lang.F_CREATE_STDOUT | lang.F_PARENT_VARTABLE) 82 val.ExitNum, err = fork.Execute(block) 83 if err != nil { 84 return val, fmt.Errorf("subshell failed: %s", err.Error()) 85 } 86 if val.ExitNum > 0 && tree.p.RunMode.IsStrict() { 87 return val, fmt.Errorf("subshell exit status %d", val.ExitNum) 88 } 89 90 switch strOrVal { 91 case varAsString: 92 err = fork.Stdout.ReadArray(tree.p.Context, func(b []byte) { 93 slice = append(slice, string(b)) 94 }) 95 case varAsValue: 96 err = fork.Stdout.ReadArrayWithType(tree.p.Context, func(v interface{}, _ string) { 97 slice = append(slice, v) 98 }) 99 default: 100 panic("invalid value set for strOrVal") 101 } 102 if err != nil { 103 return val, fmt.Errorf("cannot read from subshell's STDOUT: %s", err.Error()) 104 } 105 106 if len(slice) == 0 && tree.StrictArrays() { 107 return nil, fmt.Errorf(errEmptyArray, "{"+string(block)+"}") 108 } 109 110 val.Value = slice 111 val.DataType = types.Json 112 return val, nil 113 }