概括
如何将单个字符串转换a "b" 'c d' $'e\nf'
为单独的参数,尊重引号并保留空格和换行符?
问题
我正在尝试读取并处理一个脚本的输出,该脚本以引用字符串的形式导出值列表(每行一个)(由 创建printf %q
)。列表中的值以空格分隔,可以用引号引起来,可以包含空格和换行符,并且计数未知。
例子:a "b" 'c d' $'e\nf'
我正在寻找一种方法来恢复 POSIX shell 脚本中的值并将它们作为 shell 函数的参数进行处理。
所需的解决方案,按优先级降序排列:
- POSIX shell 命令和核心实用程序
xargs
,printf
例如 等;不eval
- 重击
- 像 perl 这样成熟的编程语言
shell 脚本的骨架process.sh
:
#!/bin/sh
set -eu
process() {
printf '>%s<\n' "${@}"
}
main() {
value_list="${1}"
# split value list here and set positional parameters
set -- ???
process "${@}"
}
main "${@}"
期望的行为
$ process.sh "a \"b\" 'c d' $'e\nf'"
>a<
>b<
>c d<
>e
f<
到目前为止发生了什么
- 我尝试了
printf
(包括%q
)、xargs
循环while read
、更改IFS
、用字节分隔参数null
、子 shell 调用、heredocs 的不同组合。 xargs
允许取消引用,但仅适用于空格,不适用于换行符,并且仅适用于不带参数的情况-d
。- 循环
while read
可以解析参数,但不允许在从管道读取输入时调用 shell 函数
答案1
外壳脚本process.sh
:
#!/bin/sh
set -eu
export script="$(readlink -f "${0}")"
split() {
zsh -c 'printf "%s\0" "${(Q@)${(z)1}}"' "${script}/split" "${@}"
}
process() {
printf '>%s<\n' "${@}"
}
main() {
value_list="${1}"
split "${value_list}" | xargs -0 sh -c 'init() { . "${script}"; } && init && process "${@}"' "${script}/main"
}
[ "${#}" -eq 0 ] || main "${@}"
好处
- 有用
缺点
- 必须获取脚本来调用
process()
formxargs
,将其封装在函数中以去除参数并防止无限循环有点尴尬 - 要求
zsh
答案2
一个简化的变体这 zsh
基于的答案:
#!/bin/zsh
set -eu
process() {
printf '>%s<\n' "${@}"
}
main() {
value_list="${1}"
process "${(Q@)${(z)value_list}}"
}
main "${@}"
好处
- 有用
缺点
- zsh 的要求