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
```yaml
'strings with spaces and a \'quote\''
strings' with spaces and a '\'quote\'
```
## Decode a base64 encoded string

View File

@ -9,7 +9,7 @@ import (
yaml "gopkg.in/yaml.v3"
)
var pattern = regexp.MustCompile(`[^\w@%+=:,./-]`)
var unsafeChars = regexp.MustCompile(`[^\w@%+=:,./-]`)
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)
}
value := originalNode.Value
if pattern.MatchString(value) {
value = "'" + strings.ReplaceAll(value, "'", "\\'") + "'"
}
return writeString(writer, value)
return writeString(writer, e.encode(originalNode.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'",
expression: ".coolData | @sh",
expected: []string{
"D0, P[coolData], (!!str)::'strings with spaces and a \\'quote\\''\n",
"D0, P[coolData], (!!str)::strings' with spaces and a '\\'quote\\'\n",
},
},
{