给定 bash 参数
foo='ab "cd" "e f" x="1 2" '
我希望生成一个相当于
foo_transformed=( ab '"cd"' '"e f"' 'x="1 2"' )
以可移植的方式,这意味着默认情况下使用 bash (v3+) 内置程序或大多数操作系统 (Linux、Unix、Cygwin) 可用的程序。给定 (几乎) 任意输入字符串,简单性和安全性是理想的。
您可以假设输入不包含单引号'
或反斜杠\
,但可能包含任意数量的空格字符,包括我希望它们分隔字符串的地方和不希望它们分隔字符串的地方(在双引号内)。
如果我们尝试:
foo_transformed=( $foo )
那么 的内部引号foo
将不受尊重 ( for k in "${foo_transformed[@]}"; do echo "- $k"; done
):
- ab
- "cd"
- "e
- f"
- x="1
- 2"
如果我们尝试:
eval foo_transformed=( $foo )
然后引号就丢失了:
- ab
- cd
- e f
- x=1 2
正则表达式允许
foo
在双引号之间包含嵌入的换行符。要正确处理其他换行符,请将 的两个实例替换\t
为\t\n
或[:space:]
。(Bash 正则表达式不接受
\t
,因此我使用$'...\t...'
语法将其转换为文字制表符/换行符。)正如评论中指出的那样,这将取决于格式错误的输入。
为了接受格式错误的输入,可以使用这个修改后的正则表达式:
或者,可以进行预检查:
这可能就是你想要的:
我给出了
foo
一些额外的值,包括通配符、单引号、潜在变量引用、新旧样式命令注入、引号字符串内外的换行符以及包含多个引号子字符串的字符串,以便可以更全面地测试脚本。上述正则表达式的使用
fpat
受到 GNU awk 的启发,FPAT
它用于识别输入中的字段,例如,请参见使用 awk 高效解析 CSV 的最可靠方法是什么?中如何在 CSV 中使用正则表达式。它匹配:(^[[:space:]]*)
- 一个可选的前导空格序列(将被丢弃),后跟与(...)
我们实际想要捕获的字符串匹配的序列:[^[:space:]]+
- 一系列非空格|
- 或者([^[:space:]"]*"([^"]|"")*"[^[:space:]"]*)+
- 包含任意字符或不包含任何字符的双引号子字符串的重复字符串,可选择由不包含空格或双引号的子字符串包围。您可以在语句中使用字符串替换
declare
,一次性完成您想要的操作:详细解释:
${foo//\"/\"\\\"}
用替换"
中的每个双引号,因此它仍然被引用,同时包含要保留的引号本身。foo
"\"
declare -a foo_transformed="(${foo//\"/\"\\\"})"
,在数组声明中使用转换后的字符串declare -a variable="(string)"
。编辑
添加了所有扩展的逃生