mirror of
https://github.com/mikefarah/yq.git
synced 2026-03-10 15:54:26 +00:00
Add --shell-key-separator flag for customizable shell output format
- Add ShellVariablesPreferences struct with KeySeparator field (default: '_') - Update shellVariablesEncoder to use configurable separator - Add --shell-key-separator CLI flag - Add comprehensive tests for custom separator functionality - Update documentation with example usage for custom separator This feature allows users to specify a custom separator (e.g. '__') when outputting shell variables, which helps disambiguate nested keys from keys that contain underscores in their names. Example: yq -o=shell --shell-key-separator='__' file.yaml Fixes ambiguity when original YAML keys contain underscores.
This commit is contained in:
parent
1228bcfa75
commit
1f2b0fe76b
@ -168,6 +168,11 @@ yq -P -oy sample.json
|
||||
rootCmd.PersistentFlags().StringVar(&yqlib.ConfiguredPropertiesPreferences.KeyValueSeparator, "properties-separator", yqlib.ConfiguredPropertiesPreferences.KeyValueSeparator, "separator to use between keys and values")
|
||||
rootCmd.PersistentFlags().BoolVar(&yqlib.ConfiguredPropertiesPreferences.UseArrayBrackets, "properties-array-brackets", yqlib.ConfiguredPropertiesPreferences.UseArrayBrackets, "use [x] in array paths (e.g. for SpringBoot)")
|
||||
|
||||
rootCmd.PersistentFlags().StringVar(&yqlib.ConfiguredShellVariablesPreferences.KeySeparator, "shell-key-separator", yqlib.ConfiguredShellVariablesPreferences.KeySeparator, "separator for shell variable key paths")
|
||||
if err = rootCmd.RegisterFlagCompletionFunc("shell-key-separator", cobra.NoFileCompletions); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
rootCmd.PersistentFlags().BoolVar(&yqlib.StringInterpolationEnabled, "string-interpolation", yqlib.StringInterpolationEnabled, "Toggles strings interpolation of \\(exp)")
|
||||
|
||||
rootCmd.PersistentFlags().BoolVarP(&nullInput, "null-input", "n", false, "Don't read input, simply evaluate the expression given. Useful for creating docs from scratch.")
|
||||
|
||||
@ -84,3 +84,23 @@ will output
|
||||
name='Miles O'"'"'Brien'
|
||||
```
|
||||
|
||||
## Encode shell variables: custom separator
|
||||
Use --shell-key-separator to specify a custom separator between keys. This is useful when the original keys contain underscores.
|
||||
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
my_app:
|
||||
db_config:
|
||||
host: localhost
|
||||
port: 5432
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq -o=shell --shell-key-separator="__" sample.yml
|
||||
```
|
||||
will output
|
||||
```sh
|
||||
my_app__db_config__host=localhost
|
||||
my_app__db_config__port=5432
|
||||
```
|
||||
|
||||
|
||||
@ -12,10 +12,13 @@ import (
|
||||
)
|
||||
|
||||
type shellVariablesEncoder struct {
|
||||
prefs ShellVariablesPreferences
|
||||
}
|
||||
|
||||
func NewShellVariablesEncoder() Encoder {
|
||||
return &shellVariablesEncoder{}
|
||||
return &shellVariablesEncoder{
|
||||
prefs: ConfiguredShellVariablesPreferences,
|
||||
}
|
||||
}
|
||||
|
||||
func (pe *shellVariablesEncoder) CanHandleAliases() bool {
|
||||
@ -58,7 +61,7 @@ func (pe *shellVariablesEncoder) doEncode(w *io.Writer, node *CandidateNode, pat
|
||||
return err
|
||||
case SequenceNode:
|
||||
for index, child := range node.Content {
|
||||
err := pe.doEncode(w, child, appendPath(path, index))
|
||||
err := pe.doEncode(w, child, pe.appendPath(path, index))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -68,7 +71,7 @@ func (pe *shellVariablesEncoder) doEncode(w *io.Writer, node *CandidateNode, pat
|
||||
for index := 0; index < len(node.Content); index = index + 2 {
|
||||
key := node.Content[index]
|
||||
value := node.Content[index+1]
|
||||
err := pe.doEncode(w, value, appendPath(path, key.Value))
|
||||
err := pe.doEncode(w, value, pe.appendPath(path, key.Value))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -81,7 +84,7 @@ func (pe *shellVariablesEncoder) doEncode(w *io.Writer, node *CandidateNode, pat
|
||||
}
|
||||
}
|
||||
|
||||
func appendPath(cookedPath string, rawKey interface{}) string {
|
||||
func (pe *shellVariablesEncoder) appendPath(cookedPath string, rawKey interface{}) string {
|
||||
|
||||
// Shell variable names must match
|
||||
// [a-zA-Z_]+[a-zA-Z0-9_]*
|
||||
@ -126,7 +129,7 @@ func appendPath(cookedPath string, rawKey interface{}) string {
|
||||
}
|
||||
return key
|
||||
}
|
||||
return cookedPath + "_" + key
|
||||
return cookedPath + pe.prefs.KeySeparator + key
|
||||
}
|
||||
|
||||
func quoteValue(value string) string {
|
||||
|
||||
@ -91,3 +91,47 @@ func TestShellVariablesEncoderEmptyMap(t *testing.T) {
|
||||
func TestShellVariablesEncoderScalarNode(t *testing.T) {
|
||||
assertEncodesTo(t, "some string", "value='some string'")
|
||||
}
|
||||
|
||||
func assertEncodesToWithSeparator(t *testing.T, yaml string, shellvars string, separator string) {
|
||||
var output bytes.Buffer
|
||||
writer := bufio.NewWriter(&output)
|
||||
|
||||
// Save the original separator
|
||||
originalSeparator := ConfiguredShellVariablesPreferences.KeySeparator
|
||||
defer func() {
|
||||
ConfiguredShellVariablesPreferences.KeySeparator = originalSeparator
|
||||
}()
|
||||
|
||||
// Set the custom separator
|
||||
ConfiguredShellVariablesPreferences.KeySeparator = separator
|
||||
|
||||
var encoder = NewShellVariablesEncoder()
|
||||
inputs, err := readDocuments(strings.NewReader(yaml), "test.yml", 0, NewYamlDecoder(ConfiguredYamlPreferences))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
node := inputs.Front().Value.(*CandidateNode)
|
||||
err = encoder.Encode(writer, node)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
writer.Flush()
|
||||
|
||||
test.AssertResult(t, shellvars, strings.TrimSuffix(output.String(), "\n"))
|
||||
}
|
||||
|
||||
func TestShellVariablesEncoderCustomSeparator(t *testing.T) {
|
||||
assertEncodesToWithSeparator(t, "a:\n b: Lewis\n c: Carroll", "a__b=Lewis\na__c=Carroll", "__")
|
||||
}
|
||||
|
||||
func TestShellVariablesEncoderCustomSeparatorNested(t *testing.T) {
|
||||
assertEncodesToWithSeparator(t, "my_app:\n db_config:\n host: localhost", "my_app__db_config__host=localhost", "__")
|
||||
}
|
||||
|
||||
func TestShellVariablesEncoderCustomSeparatorArray(t *testing.T) {
|
||||
assertEncodesToWithSeparator(t, "a: [{n: Alice}, {n: Bob}]", "a__0__n=Alice\na__1__n=Bob", "__")
|
||||
}
|
||||
|
||||
func TestShellVariablesEncoderCustomSeparatorSingleChar(t *testing.T) {
|
||||
assertEncodesToWithSeparator(t, "a:\n b: value", "aXb=value", "X")
|
||||
}
|
||||
|
||||
14
pkg/yqlib/shellvariables.go
Normal file
14
pkg/yqlib/shellvariables.go
Normal file
@ -0,0 +1,14 @@
|
||||
package yqlib
|
||||
|
||||
type ShellVariablesPreferences struct {
|
||||
KeySeparator string
|
||||
}
|
||||
|
||||
func NewDefaultShellVariablesPreferences() ShellVariablesPreferences {
|
||||
return ShellVariablesPreferences{
|
||||
KeySeparator: "_",
|
||||
}
|
||||
}
|
||||
|
||||
var ConfiguredShellVariablesPreferences = NewDefaultShellVariablesPreferences()
|
||||
|
||||
@ -54,12 +54,33 @@ var shellVariablesScenarios = []formatScenario{
|
||||
input: "name: Miles O'Brien",
|
||||
expected: `name='Miles O'"'"'Brien'` + "\n",
|
||||
},
|
||||
{
|
||||
description: "Encode shell variables: custom separator",
|
||||
subdescription: "Use --shell-key-separator to specify a custom separator between keys. This is useful when the original keys contain underscores.",
|
||||
input: "" +
|
||||
"my_app:" + "\n" +
|
||||
" db_config:" + "\n" +
|
||||
" host: localhost" + "\n" +
|
||||
" port: 5432",
|
||||
expected: "" +
|
||||
"my_app__db_config__host=localhost" + "\n" +
|
||||
"my_app__db_config__port=5432" + "\n",
|
||||
scenarioType: "shell-separator",
|
||||
},
|
||||
}
|
||||
|
||||
func TestShellVariableScenarios(t *testing.T) {
|
||||
for _, s := range shellVariablesScenarios {
|
||||
//fmt.Printf("\t<%s> <%s>\n", s.expected, mustProcessFormatScenario(s, NewYamlDecoder(ConfiguredYamlPreferences), NewShellVariablesEncoder()))
|
||||
test.AssertResultWithContext(t, s.expected, mustProcessFormatScenario(s, NewYamlDecoder(ConfiguredYamlPreferences), NewShellVariablesEncoder()), s.description)
|
||||
if s.scenarioType == "shell-separator" {
|
||||
// Save and restore the original separator
|
||||
originalSeparator := ConfiguredShellVariablesPreferences.KeySeparator
|
||||
ConfiguredShellVariablesPreferences.KeySeparator = "__"
|
||||
test.AssertResultWithContext(t, s.expected, mustProcessFormatScenario(s, NewYamlDecoder(ConfiguredYamlPreferences), NewShellVariablesEncoder()), s.description)
|
||||
ConfiguredShellVariablesPreferences.KeySeparator = originalSeparator
|
||||
} else {
|
||||
test.AssertResultWithContext(t, s.expected, mustProcessFormatScenario(s, NewYamlDecoder(ConfiguredYamlPreferences), NewShellVariablesEncoder()), s.description)
|
||||
}
|
||||
}
|
||||
genericScenarios := make([]interface{}, len(shellVariablesScenarios))
|
||||
for i, s := range shellVariablesScenarios {
|
||||
@ -87,12 +108,22 @@ func documentShellVariableScenario(_ *testing.T, w *bufio.Writer, i interface{})
|
||||
|
||||
expression := s.expression
|
||||
|
||||
if expression != "" {
|
||||
if s.scenarioType == "shell-separator" {
|
||||
writeOrPanic(w, "```bash\nyq -o=shell --shell-key-separator=\"__\" sample.yml\n```\n")
|
||||
} else if expression != "" {
|
||||
writeOrPanic(w, fmt.Sprintf("```bash\nyq -o=shell '%v' sample.yml\n```\n", expression))
|
||||
} else {
|
||||
writeOrPanic(w, "```bash\nyq -o=shell sample.yml\n```\n")
|
||||
}
|
||||
writeOrPanic(w, "will output\n")
|
||||
|
||||
writeOrPanic(w, fmt.Sprintf("```sh\n%v```\n\n", mustProcessFormatScenario(s, NewYamlDecoder(ConfiguredYamlPreferences), NewShellVariablesEncoder())))
|
||||
if s.scenarioType == "shell-separator" {
|
||||
// Save and restore the original separator
|
||||
originalSeparator := ConfiguredShellVariablesPreferences.KeySeparator
|
||||
ConfiguredShellVariablesPreferences.KeySeparator = "__"
|
||||
writeOrPanic(w, fmt.Sprintf("```sh\n%v```\n\n", mustProcessFormatScenario(s, NewYamlDecoder(ConfiguredYamlPreferences), NewShellVariablesEncoder())))
|
||||
ConfiguredShellVariablesPreferences.KeySeparator = originalSeparator
|
||||
} else {
|
||||
writeOrPanic(w, fmt.Sprintf("```sh\n%v```\n\n", mustProcessFormatScenario(s, NewYamlDecoder(ConfiguredYamlPreferences), NewShellVariablesEncoder())))
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user