Description
The typeset/declare parser (added in v1.25.7) silently drops any scalar variable whose value starts with [ or whose value is an empty string. The variable simply does not appear in the output — no error, no warning.
The root cause is the value sub-pattern in VAR_DEF_PATTERN:
https://github.com/kellyjonbrazil/jc/blob/master/jc/parsers/typeset.py#L136
VAR_DEF_PATTERN = re.compile(r'(?P<name>[a-zA-Z_][a-zA-Z0-9_]*)=(?P<val>[^(][^[].+)$')
In declare -p output the value is wrapped in quotes, so the value group sees "...". The three parts [^(] [^[] .+ mean:
[^(] matches the opening quote " (this correctly excludes array bodies that start with (),
[^[] then matches the first character of the actual value and forbids it from being [,
.+ forces the remaining value to be at least one more character (so "" — an empty string — cannot match either).
So a legitimate scalar whose value begins with [, or an empty string, fails VAR_DEF_PATTERN, matches none of the other patterns (they all require the value to start with (), and is dropped.
Steps to Reproduce
import jc.parsers.typeset as t
# value starts with '[' -> dropped
print(t.parse('declare -- a="[x]"', quiet=True)) # []
# empty string value -> dropped
print(t.parse('declare -- e=""', quiet=True)) # []
# control: normal value -> works
print(t.parse('declare -- ok="hello"', quiet=True)) # [{... 'value': 'hello' ...}]
Silent data loss in a realistic multi-variable dump:
data = '''declare -- var1="hello"
declare -- var2="[bracketed]"
declare -- var3="world"'''
out = t.parse(data, quiet=True)
print(len(out)) # 2 (expected 3 — var2 is silently missing)
These are all valid bash values, e.g.:
$ mystr="[test]"; declare -p mystr
declare -- mystr="[test]"
Expected Behavior
All three variables should be parsed; var2 should appear with "value": "[bracketed]", and empty-string variables should appear with "value": "".
Actual Behavior
var2 (and empty-string variables) are silently omitted from the output.
Suggested Fix
The [^[] second-character restriction and the .+ minimum-length requirement are unnecessary — the only thing needed to distinguish a scalar from an array is that the value does not start with (. Changing the value group to [^(].* fixes both cases while still routing array definitions to the array patterns:
VAR_DEF_PATTERN = re.compile(r'(?P<name>[a-zA-Z_][a-zA-Z0-9_]*)=(?P<val>[^(].*)$')
Verified against the affected inputs:
| input |
[^(].* result |
declare -- a="[x]" |
name=a, val="[x]" ✓ |
declare -- e="" |
name=e, val="" ✓ |
declare -- ok="(paren)" |
name=ok, val="(paren)" ✓ (still scalar) |
declare -a arr=([0]="x") |
no match ✓ (falls through to array pattern) |
Environment
- jc version: 1.25.7 (current
master, verified via git clone --depth 1)
- Python: 3.12
Description
The
typeset/declareparser (added in v1.25.7) silently drops any scalar variable whose value starts with[or whose value is an empty string. The variable simply does not appear in the output — no error, no warning.The root cause is the value sub-pattern in
VAR_DEF_PATTERN:https://github.com/kellyjonbrazil/jc/blob/master/jc/parsers/typeset.py#L136
In
declare -poutput the value is wrapped in quotes, so the value group sees"...". The three parts[^(][^[].+mean:[^(]matches the opening quote"(this correctly excludes array bodies that start with(),[^[]then matches the first character of the actual value and forbids it from being[,.+forces the remaining value to be at least one more character (so""— an empty string — cannot match either).So a legitimate scalar whose value begins with
[, or an empty string, failsVAR_DEF_PATTERN, matches none of the other patterns (they all require the value to start with(), and is dropped.Steps to Reproduce
Silent data loss in a realistic multi-variable dump:
These are all valid bash values, e.g.:
Expected Behavior
All three variables should be parsed;
var2should appear with"value": "[bracketed]", and empty-string variables should appear with"value": "".Actual Behavior
var2(and empty-string variables) are silently omitted from the output.Suggested Fix
The
[^[]second-character restriction and the.+minimum-length requirement are unnecessary — the only thing needed to distinguish a scalar from an array is that the value does not start with(. Changing the value group to[^(].*fixes both cases while still routing array definitions to the array patterns:Verified against the affected inputs:
[^(].*resultdeclare -- a="[x]"a, val="[x]"✓declare -- e=""e, val=""✓declare -- ok="(paren)"ok, val="(paren)"✓ (still scalar)declare -a arr=([0]="x")Environment
master, verified viagit clone --depth 1)