`parse()` does not support bash ANSI-C quoting (`$'...'`)
- Dominant language
- JavaScript
- Stars
- 62
- Forks
- 23
- PR merge metrics
- No merged PRs in 30d
Description
## Summary
`shell-quote` (reproduced on **v1.10.0**) does not implement bash's [ANSI-C quoting](https://www.gnu.org/software/bash/manual/html_node/ANSI_002dC-Quoting.html) — the `$'...'` form. This is not an exotic construct: it is exactly what Chrome/Edge DevTools emit from *Copy as cURL (bash)* (e.g. `--data-raw $'...'`), so any consumer that uses `shell-quote` to parse real-world copied shell commands silently gets the wrong arguments.
## Current behaviour
The scanner treats the leading `$` as the start of a parameter expansion (empty name → emits a literal `$`), and then treats the rest of the token as a plain single-quoted string. Consequences:
1. the leading `$` of the `$'` prefix is kept in the output (it should be dropped);
2. ANSI-C escape sequences are **not** decoded (`\n`, `\t`, `\x41`, `\'`, …);
3. `\'` does not escape the quote — it terminates the string and the remainder is mis-parsed.
## Reproduction
```js
const { parse } = require('shell-quote'); // v1.10.0
const cases = [
'$' + "'" + '{"a":"${field}"}' + "'",
'$' + "'" + 'line1\\nline2' + "'",
'$' + "'" + 'a\\\'b' + "'",
'$' + "'" + 'tab\\there' + "'",
'$' + "'" + '\\x41' + "'",
'$' + "'" + 'cost is $5' + "'"
];
cases.forEach((c) => console.log(JSON.stringify(c), '->', JSON.stringify(parse(c)[0])));
```
| input | bash output (`printf '%s' `) | `shell-quote@1.10.0` |
| --- | --- | --- |
| `$'{"a":"${field}"}'` | `{"a":"${field}"}` | `${"a":"${field}"}` ❌ leading `$` kept |
| `$'line1\nline2'` | `line1` ⏎ `line2` | `$line1\nline2` ❌ leading `$` + escape not decoded |
| `$'a\'b'` | `a'b` | `$a\b` ❌ (quote terminated by `\'`, `\` kept) |
| `$'tab\there'` | `tab`⇥`here` | `$tab\there` ❌ |
| `$'\x41'` | `A` | `$\x41` ❌ |
| `$'cost is $5'` | `cost is $5` | `$cost is $5` ❌ leading `$` kept |
## Expected behaviour
Per the bash manual, inside `$'...'`:
- the documented escape sequences are decoded (`\n`, `\t`, `\'`, `\"`, `\\`, `\nnn`, `\xHH`, `\uHHHH`, `\UHHHHHHHH`, `\cx`, `\a`, `\b`, `\e`, `\E`, `\f`, `\r`, `\v`, `\?`, …);
- **no** parameter expansion, command substitution or brace expansion happens — `${field}` and `$5` stay literal;
- the `$'` prefix and the surrounding quotes are removed, and the token is not word-split.
## Real-world impact / related
- postmanlabs/curl-to-postman#104 — Postman's cURL importer corrupted literal `${...}` in Chrome-copied cURL commands. It uses a **vendored copy** of `shell-quote` (`assets/shell-quote.js`), patched to decode escapes; because that patch kept the double-quote branch's variable resolution, `${field}` was rendered as `$field`. Fix: postmanlabs/curl-to-postman#105.
- If `parse()` supported `$'...'` natively, downstream consumers (Postman included) would not need to vendor + patch this file.
- Nice-to-have on the other side: `quote()` could emit `$'...'` for strings containing a single quote (instead of `'\''`), which is what bash/DevTools do and what round-trips through the above fix.
Thanks for maintaining this library!
Contributor guide
Research direction
Start with the parse() entry point and reproduce the listed ANSI-C quoting cases. Compare the results with the documented Bash behavior, including escape decoding and literal parameter text; done means the prefix and surrounding quotes are removed, escapes decode correctly, and the token remains unsplit.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- bash, javascript
- Domain
- cli
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 55/100