func FromCommandParams(cmd *cobra.Command, prefix string, addShortcuts bool) func() (Externals, error) {
	var (
		extStrings  strFiles
		extCodes    strFiles
		tlaStrings  strFiles
		tlaCodes    strFiles
		paths       []string
		dataSources []string
	)
	fs := cmd.PersistentFlags()
	if addShortcuts {
		fs.StringArrayVarP(&extStrings.strings, prefix+"ext-str", "V", nil, "external string: <var>=[val], if <val> is omitted, get from environment var <var>")
	} else {
		fs.StringArrayVar(&extStrings.strings, prefix+"ext-str", nil, "external string: <var>=[val], if <val> is omitted, get from environment var <var>")
	}
	fs.StringArrayVar(&extStrings.files, prefix+"ext-str-file", nil, "external string from file: <var>=<filename>")
	fs.StringArrayVar(&extStrings.lists, prefix+"ext-str-list", nil, "file containing lines of the form <var>[=<val>]")
	fs.StringArrayVar(&extCodes.strings, prefix+"ext-code", nil, "external code: <var>=[val], if <val> is omitted, get from environment var <var>")
	fs.StringArrayVar(&extCodes.files, prefix+"ext-code-file", nil, "external code from file: <var>=<filename>")
	if addShortcuts {
		fs.StringArrayVarP(&tlaStrings.strings, prefix+"tla-str", "A", nil, "top-level string: <var>=[val], if <val> is omitted, get from environment var <var>")
	} else {
		fs.StringArrayVar(&tlaStrings.strings, prefix+"tla-str", nil, "top-level string: <var>=[val], if <val> is omitted, get from environment var <var>")
	}
	fs.StringArrayVar(&tlaStrings.files, prefix+"tla-str-file", nil, "top-level string from file: <var>=<filename>")
	fs.StringArrayVar(&tlaStrings.lists, prefix+"tla-str-list", nil, "file containing lines of the form <var>[=<val>]")
	fs.StringArrayVar(&tlaCodes.strings, prefix+"tla-code", nil, "top-level code: <var>=[val], if <val> is omitted, get from environment var <var>")
	fs.StringArrayVar(&tlaCodes.files, prefix+"tla-code-file", nil, "top-level code from file: <var>=<filename>")
	fs.StringArrayVar(&paths, prefix+"jpath", nil, "additional jsonnet library path")
	fs.StringArrayVar(&dataSources, prefix+"data-source", nil, "additional data sources in URL format")
	return func() (c Externals, err error) {
		vars := map[string]UserVal{}
		tlaVars := map[string]UserVal{}
		if err = getValues(vars, "ext-str", extStrings, newVal); err != nil {
			return
		}
		if err = getValues(vars, "ext-code", extCodes, newCodeVal); err != nil {
			return
		}
		if err = getValues(tlaVars, "tla-str", tlaStrings, newVal); err != nil {
			return
		}
		if err = getValues(tlaVars, "tla-code", tlaCodes, newCodeVal); err != nil {
			return
		}
		c.Variables.Vars = vars
		c.Variables.TopLevelVars = tlaVars
		c.LibPaths = paths
		c.DataSources = dataSources
		return
	}
}
FromCommandParams attaches VM related flags to the specified command and returns
a function that provides the config based on command line flags.
package vmexternals
import (
	"bufio"
	"bytes"
	"fmt"
	"io/ioutil"
	"os"
	"strings"
	"github.com/pkg/errors"
	"github.com/spf13/cobra"
	"github.com/splunk/qbec/vm"
)
type UserVal struct {
	Value string  
	Code  bool    
}
func newVal(value string) UserVal {
	return UserVal{Value: value}
}
func newCodeVal(code string) UserVal {
	return UserVal{Value: code, Code: true}
}
type UserVariables struct {
	Vars         map[string]UserVal  
	TopLevelVars map[string]UserVal  
}
type Externals struct {
	Variables   UserVariables  
	LibPaths    []string       
	DataSources []string       
}
func (c Externals) WithLibPaths(paths []string) Externals {
	return Externals{Variables: c.Variables, LibPaths: append(c.LibPaths, paths...)}
}
func (c Externals) ToVariableSet() vm.VariableSet {
	ret := vm.VariableSet{}
	var vars []vm.Var
	for name, v := range c.Variables.Vars {
		if v.Code {
			vars = append(vars, vm.NewCodeVar(name, v.Value))
		} else {
			vars = append(vars, vm.NewVar(name, v.Value))
		}
	}
	var tlaVars []vm.Var
	for name, v := range c.Variables.TopLevelVars {
		if v.Code {
			tlaVars = append(tlaVars, vm.NewCodeVar(name, v.Value))
		} else {
			tlaVars = append(tlaVars, vm.NewVar(name, v.Value))
		}
	}
	return ret.WithVars(vars...).WithTopLevelVars(tlaVars...)
}
type strFiles struct {
	strings []string
	files   []string
	lists   []string
}
func getValues(ret map[string]UserVal, name string, s strFiles, fn func(value string) UserVal) error {
	processStr := func(s string, ctx string) error {
		parts := strings.SplitN(s, "=", 2)
		if len(parts) == 2 {
			ret[parts[0]] = fn(parts[1])
			return nil
		}
		v, ok := os.LookupEnv(s)
		if !ok {
			return fmt.Errorf("%sno value found from environment for %s", ctx, s)
		}
		ret[s] = fn(v)
		return nil
	}
	processFile := func(s string) error {
		parts := strings.SplitN(s, "=", 2)
		if len(parts) == 1 {
			return fmt.Errorf("%s-file no filename specified for %s", name, s)
		}
		b, err := ioutil.ReadFile(parts[1])
		if err != nil {
			return err
		}
		ret[parts[0]] = fn(string(b))
		return nil
	}
	processList := func(l string) error {
		b, err := ioutil.ReadFile(l)
		if err != nil {
			return err
		}
		scanner := bufio.NewScanner(bytes.NewReader(b))
		num := 0
		for scanner.Scan() {
			num++
			line := scanner.Text()
			if line != "" {
				err := processStr(line, "")
				if err != nil {
					return errors.Wrap(err, fmt.Sprintf("process list %s, line %d", l, num))
				}
			}
		}
		if err := scanner.Err(); err != nil {
			return errors.Wrap(err, fmt.Sprintf("process list %s", l))
		}
		return nil
	}
	for _, s := range s.lists {
		if err := processList(s); err != nil {
			return err
		}
	}
	for _, s := range s.strings {
		if err := processStr(s, name+" "); err != nil {
			return err
		}
	}
	for _, s := range s.files {
		if err := processFile(s); err != nil {
			return err
		}
	}
	return nil
}