Skip to content

Feature Request: Add --shell-key-separator flag for customizable shell output format separator #2497

@rsleedbx

Description

@rsleedbx

Problem / Use Case

When using --output-format=shell (or -o=shell), yq collapses nested keys using a single underscore _ as the separator. For example:

my_app:
  database_config:
    host: localhost

becomes:

my_app_database_config_host=localhost

This creates ambiguity when the original YAML keys themselves contain underscores. It's impossible to distinguish between:

  • A nested structure like my.app.database (3 keys)
  • A flat structure like my_app.database (2 keys, where first key contains _)
  • A single key like my_app_database (1 key)

This is particularly problematic when:

  1. Working with YAML files that use snake_case naming conventions
  2. Sourcing the shell variables and then trying to reconstruct the original YAML structure
  3. Using the shell output in scripts that need to parse the variable names

Proposed Solution

Add a --shell-key-separator flag (similar to existing --properties-separator and --csv-separator flags) that allows users to specify a custom separator for collapsing nested keys in shell output format.

Default behavior: _ (single underscore) - maintains backward compatibility

Example usage:

yq -o=shell --shell-key-separator="__" file.yaml

Examples

Given sample.yml:

my_app:
  db_config:
    host: localhost
    port: 5432

Current behavior (yq -o=shell sample.yml):

my_app_db_config_host=localhost
my_app_db_config_port=5432

Problem: Three underscores - hard to tell where the original key boundaries are

Proposed behavior (yq -o=shell --shell-key-separator="__" sample.yml):

my_app__db_config__host=localhost
my_app__db_config__port=5432

Clear: Double underscores indicate nesting, single underscores are part of the original key names

Implementation Notes

Following the pattern of similar flags in yq:

  1. Add ShellVariablesPreferences struct (similar to PropertiesPreferences, CSVPreferences, etc.):

    type ShellVariablesPreferences struct {
        KeySeparator string
    }
    
    func NewDefaultShellVariablesPreferences() ShellVariablesPreferences {
        return ShellVariablesPreferences{
            KeySeparator: "_",
        }
    }
    
    var ConfiguredShellVariablesPreferences = NewDefaultShellVariablesPreferences()
  2. Update shellVariablesEncoder to accept preferences:

    func NewShellVariablesEncoder() Encoder {
        return &shellVariablesEncoder{
            prefs: ConfiguredShellVariablesPreferences,
        }
    }
  3. Update appendPath function (line 129 in encoder_shellvariables.go):

    // Current:
    return cookedPath + "_" + key
    
    // Proposed:
    return cookedPath + pe.prefs.KeySeparator + key
  4. Add CLI flag in cmd/root.go:

    rootCmd.PersistentFlags().StringVar(
        &yqlib.ConfiguredShellVariablesPreferences.KeySeparator, 
        "shell-key-separator", 
        yqlib.ConfiguredShellVariablesPreferences.KeySeparator, 
        "separator for shell variable key paths",
    )

Backward Compatibility

Fully backward compatible - default value of _ maintains existing behavior

Related Flags

This follows the same pattern as existing separator flags:

  • --properties-separator - separator between keys and values in properties format (default " = ")
  • --csv-separator - CSV separator character (default ,)

Validation Considerations

The separator should be validated to ensure it produces valid shell variable names:

  • Must only contain characters allowed in shell variable names: [a-zA-Z0-9_]
  • Should not be empty
  • Suggested validation in Set() method or when flag is parsed

Additional Benefits

This flag would also help when:

  • Generating shell variables for consumption by other tools expecting specific naming conventions
  • Working with configuration management systems that have their own naming standards
  • Debugging - easier to visually parse variable names with custom separators

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions