Use a lazy-quoting @sh encoder

This commit is contained in:
Vit Zikmund 2023-02-06 12:36:12 +00:00
parent 88a6b20ba5
commit c2f7e0d82c
3 changed files with 38 additions and 8 deletions

View File

@ -478,7 +478,7 @@ yq '.coolData | @sh' sample.yml
``` ```
will output will output
```yaml ```yaml
'strings with spaces and a \'quote\'' strings' with spaces and a '\'quote\'
``` ```
## Decode a base64 encoded string ## Decode a base64 encoded string

View File

@ -9,7 +9,7 @@ import (
yaml "gopkg.in/yaml.v3" yaml "gopkg.in/yaml.v3"
) )
var pattern = regexp.MustCompile(`[^\w@%+=:,./-]`) var unsafeChars = regexp.MustCompile(`[^\w@%+=:,./-]`)
type shEncoder struct { type shEncoder struct {
} }
@ -36,9 +36,39 @@ func (e *shEncoder) Encode(writer io.Writer, originalNode *yaml.Node) error {
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) 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 return writeString(writer, e.encode(originalNode.Value))
if pattern.MatchString(value) {
value = "'" + strings.ReplaceAll(value, "'", "\\'") + "'"
} }
return writeString(writer, value)
// put any shell-unsafe characters into a single-quoted block, close the block lazily
func (e *shEncoder) encode(input string) string {
const quote = '\''
var inQuoteBlock = false
var encoded strings.Builder
encoded.Grow(len(input))
for _, ir := range input {
// open or close a single-quote block
if ir == quote {
if inQuoteBlock {
// get out of a quote block for an input quote
encoded.WriteRune(quote)
inQuoteBlock = !inQuoteBlock
}
// escape the quote with a backslash
encoded.WriteRune('\\')
} else {
if unsafeChars.MatchString(string(ir)) && !inQuoteBlock {
// start a quote block on an unsafe character
encoded.WriteRune(quote)
inQuoteBlock = !inQuoteBlock
}
}
// pass on the input character
encoded.WriteRune(ir)
}
// close any pending quote block
if inQuoteBlock {
encoded.WriteRune(quote)
}
return encoded.String()
} }

View File

@ -263,7 +263,7 @@ var encoderDecoderOperatorScenarios = []expressionScenario{
document: "coolData: strings with spaces and a 'quote'", document: "coolData: strings with spaces and a 'quote'",
expression: ".coolData | @sh", expression: ".coolData | @sh",
expected: []string{ expected: []string{
"D0, P[coolData], (!!str)::'strings with spaces and a \\'quote\\''\n", "D0, P[coolData], (!!str)::strings' with spaces and a '\\'quote\\'\n",
}, },
}, },
{ {