Suggestions about arguments escaping and quoting #874
-
Even though my examples are all about PowerShell, I guess some kind of similar mechanism would be usefull for bash too... Verbatim strings, expandable strings, and here-stringsFor now, For example, the following code does not work as expected (meaning the output won't be blue) unless import { usePwsh } from 'zx'
usePwsh()
await $`Write-Host ${'$($PSStyle.Foreground.Blue)This should be blue!$($PSStyle.Reset)'}` Note that the $.quote = (arg) => {
if (arg.indexOf('\n') !== -1) return `@"\n${arg}\n"@`
return /^[a-z0-9/_.\-]+$/i.test(arg) || arg === ''
? arg
: `"${arg.replace(/"/g, '`"')}"`
} But I guess it would be event better if usePwsh({ quote: 'expandable' }) More control over escaping and quotingIMHO, it would also be interesting to let the user have control over escaping and quoting mechanism at the "argument" level. import { usePwsh, expandable } from 'zx'
usePwsh()
await $`Write-Host ${expandable(
'$($PSStyle.Foreground.Blue)This should be blue!$($PSStyle.Reset)'
)}` Working exampleHere is a working example (with an awfull hack, though) illustrating what I mean: import assert from 'node:assert'
const verbatimQuote = (arg) => {
if (arg.indexOf('\n') !== -1) return `@'\n${arg}\n'@`
return /^[a-z0-9/_.\-]+$/i.test(arg) || arg === ''
? arg
: `'${arg.replace(/'/g, "''")}'`
}
const expandableQuote = (arg) => {
if (arg.indexOf('\n') !== -1) return `@"\n${arg}\n"@`
return /^[a-z0-9/_.\-]+$/i.test(arg) || arg === ''
? arg
: `"${arg.replace(/"/g, '`"')}"`
}
let defaultQuote = verbatimQuote
const usePwsh = ({ quote } = { quote: 'verbatim' }) => {
$.shell = which.sync('pwsh.exe')
$.prefix = ''
$.postfix = '; exit $LastExitCode'
switch (quote) {
case 'verbatim':
defaultQuote = verbatimQuote
break
case 'expandable':
defaultQuote = expandableQuote
break
}
}
class Quote {
quote
string
constructor(q, s) {
switch (q) {
case 'verbatim':
this.quote = verbatimQuote
break
case 'expandable':
this.quote = expandableQuote
break
default:
assert(typeof q === 'function')
this.quote = q
break
}
this.string = s
}
// This is an awfull hack that should be replaced by an evolution of the 'substitute' function in zurk:
//
// ```js
// export const substitute = (arg) =>
// typeof arg && arg.stdout === 'string'
// ? arg.stdout.replace(/\n$/, '')
// : arg instanceof Quote
// ? arg
// : `${arg}`
// ```
static MARK = 'QUOTE_MARK_HACK'
static isQuoted(s) {
return s.startsWith(Quote.MARK)
}
static get(s) {
return s.substring(Quote.MARK.length)
}
static mark(s) {
return `${Quote.MARK}${s}`
}
toString() {
return Quote.mark(this.quote(this.string))
}
}
$.quote = (arg) => {
if (Quote.isQuoted(arg)) return Quote.get(arg)
return defaultQuote(arg)
}
const quote = (q, s) => new Quote(q, s)
const verbatim = (s) => quote('verbatim', s)
const expandable = (s) => quote('expandable', s)
usePwsh()
$.verbose = true
await $`Write-Host ${'verbatim string'}`
await $`Write-Host ${`verbatim
here-string`}`
await $`Write-Host ${expandable(
`$($PSStyle.Foreground.Blue)expandable string$($PSStyle.Reset)`
)}`
await $`Write-Host ${expandable(
`$($PSStyle.Foreground.Blue)expandable
here-string$($PSStyle.Reset)`
)}`
await $`Write-Host ${quote(
(s) => s,
`@"
Escaping and quoting mechanism is disabled here.
Which allows the user doing it by himself if needed.
"@`
)}` |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 9 replies
-
What about instead of argument, use a different quote func?
|
Beta Was this translation helpful? Give feedback.
Allowing this makes zx less strict and puts a security risk. For example,
message
can contain other$
-Style variables which will be printed (and this is not wanted). Although Window usage in Google is low, it is still significant. We would not like to have insecure faults.