diff --git a/pkg/yqlib/doc/operators/encode-decode.md b/pkg/yqlib/doc/operators/encode-decode.md index f6be772c..93b7abb4 100644 --- a/pkg/yqlib/doc/operators/encode-decode.md +++ b/pkg/yqlib/doc/operators/encode-decode.md @@ -17,6 +17,7 @@ These operators are useful to process yaml documents that have stringified embed | XML | from_xml/@xmld | to_xml(i)/@xml | | Base64 | @base64d | @base64 | | URI | @urid | @uri | +| Shell | | @sh | See CSV and TSV [documentation](https://mikefarah.gitbook.io/yq/usage/csv-tsv) for accepted formats. @@ -464,6 +465,22 @@ will output this has & special () characters * ``` +## Encode a string to sh +Sh/Bash friendly string + +Given a sample.yml file of: +```yaml +coolData: strings with spaces and a 'quote' +``` +then +```bash +yq '.coolData | @sh' sample.yml +``` +will output +```yaml +'strings with spaces and a \'quote\'' +``` + ## Decode a base64 encoded string Decoded data is assumed to be a string. diff --git a/pkg/yqlib/doc/operators/headers/encode-decode.md b/pkg/yqlib/doc/operators/headers/encode-decode.md index 31c1c10b..b5108de4 100644 --- a/pkg/yqlib/doc/operators/headers/encode-decode.md +++ b/pkg/yqlib/doc/operators/headers/encode-decode.md @@ -17,6 +17,7 @@ These operators are useful to process yaml documents that have stringified embed | XML | from_xml/@xmld | to_xml(i)/@xml | | Base64 | @base64d | @base64 | | URI | @urid | @uri | +| Shell | | @sh | See CSV and TSV [documentation](https://mikefarah.gitbook.io/yq/usage/csv-tsv) for accepted formats. diff --git a/pkg/yqlib/encoder_sh.go b/pkg/yqlib/encoder_sh.go new file mode 100644 index 00000000..53663158 --- /dev/null +++ b/pkg/yqlib/encoder_sh.go @@ -0,0 +1,44 @@ +package yqlib + +import ( + "fmt" + "io" + "regexp" + "strings" + + yaml "gopkg.in/yaml.v3" +) + +var pattern = regexp.MustCompile(`[^\w@%+=:,./-]`) + +type shEncoder struct { +} + +func NewShEncoder() Encoder { + return &shEncoder{} +} + +func (e *shEncoder) CanHandleAliases() bool { + return false +} + +func (e *shEncoder) PrintDocumentSeparator(writer io.Writer) error { + return nil +} + +func (e *shEncoder) PrintLeadingContent(writer io.Writer, content string) error { + return nil +} + +func (e *shEncoder) Encode(writer io.Writer, originalNode *yaml.Node) error { + node := unwrapDoc(originalNode) + if guessTagFromCustomType(node) != "!!str" { + return fmt.Errorf("cannot encode %v as URI, can only operate on strings. Please first pipe through another encoding operator to convert the value to a string", node.Tag) + } + + value := originalNode.Value + if pattern.MatchString(value) { + value = "'" + strings.ReplaceAll(value, "'", "\\'") + "'" + } + return writeString(writer, value) +} diff --git a/pkg/yqlib/lexer_participle.go b/pkg/yqlib/lexer_participle.go index ff6b203e..09e9dd75 100644 --- a/pkg/yqlib/lexer_participle.go +++ b/pkg/yqlib/lexer_participle.go @@ -80,6 +80,7 @@ var participleYqRules = []*participleYqRule{ {"Urid", `@urid`, decodeOp(UriInputFormat), 0}, {"Uri", `@uri`, encodeWithIndent(UriOutputFormat, 0), 0}, + {"SH", `@sh`, encodeWithIndent(ShOutputFormat, 0), 0}, {"LoadXML", `load_?xml|xml_?load`, loadOp(NewXMLDecoder(ConfiguredXMLPreferences), false), 0}, diff --git a/pkg/yqlib/operator_encoder_decoder.go b/pkg/yqlib/operator_encoder_decoder.go index 2fa0cb05..255b0664 100644 --- a/pkg/yqlib/operator_encoder_decoder.go +++ b/pkg/yqlib/operator_encoder_decoder.go @@ -28,6 +28,8 @@ func configureEncoder(format PrinterOutputFormat, indent int) Encoder { return NewBase64Encoder() case UriOutputFormat: return NewUriEncoder() + case ShOutputFormat: + return NewShEncoder() } panic("invalid encoder") } diff --git a/pkg/yqlib/operator_encoder_decoder_test.go b/pkg/yqlib/operator_encoder_decoder_test.go index ee54868e..7801e6cf 100644 --- a/pkg/yqlib/operator_encoder_decoder_test.go +++ b/pkg/yqlib/operator_encoder_decoder_test.go @@ -257,6 +257,15 @@ var encoderDecoderOperatorScenarios = []expressionScenario{ "D0, P[], (!!str)::this has & special () characters *\n", }, }, + { + description: "Encode a string to sh", + subdescription: "Sh/Bash friendly string", + document: "coolData: strings with spaces and a 'quote'", + expression: ".coolData | @sh", + expected: []string{ + "D0, P[coolData], (!!str)::'strings with spaces and a \\'quote\\''\n", + }, + }, { description: "Decode a base64 encoded string", subdescription: "Decoded data is assumed to be a string.", diff --git a/pkg/yqlib/printer.go b/pkg/yqlib/printer.go index 14f96cc3..802793a9 100644 --- a/pkg/yqlib/printer.go +++ b/pkg/yqlib/printer.go @@ -28,6 +28,7 @@ const ( XMLOutputFormat Base64OutputFormat UriOutputFormat + ShOutputFormat ) func OutputFormatFromString(format string) (PrinterOutputFormat, error) {