这是我的问题。在bash 3中:
$ test='One "This is two" Three'
$ set -- $test
$ echo $2
"This
如何让bash理解引号并将{2}作为This is two
而不是"This
返回?不幸的是,在这个例子中,我不能改变名为test
的变量的构造。
答案 0 :(得分:8)
发生这种情况的原因是shell解析命令行的顺序:它解析(并删除)引号和转义,然后替换变量值。当$test
被One "This is two" Three
取代时,引号才能产生预期效果为时已晚。
执行此操作的简单(但危险)方法是使用eval
添加另一级解析:
$ test='One "This is two" Three'
$ eval "set -- $test"
$ echo "$2"
This is two
(请注意,echo
命令中的引号不是必需的,但这是一个很好的通用做法。)
我说这很危险的原因是它不仅仅会返回并重新分析引用的字符串,它会返回并重新分析所有内容,可能包括您不希望解释为命令的内容换人。假设你已经设置了
$ test='One `rm /some/important/file` Three'
... eval
实际上会运行rm
命令。因此,如果您不能指望$test
的内容为“安全”,请勿使用此构造。
$ test=(One "This is two" Three)
$ set -- "${test[@]}"
$ echo "$2"
This is two
不幸的是,这需要控制变量的创建方式。
答案 1 :(得分:6)
现在我们有bash 4可以做类似的事情:
#!/bin/bash
function qs_parse() {
readarray -t "$1" < <( printf "%s" "$2"|xargs -n 1 printf "%s\n" )
}
tab=' ' # tabulation here
qs_parse test "One 'This is two' Three -n 'foo${tab}bar'"
printf "%s\n" "${test[0]}"
printf "%s\n" "${test[1]}"
printf "%s\n" "${test[2]}"
printf "%s\n" "${test[3]}"
printf "%s\n" "${test[4]}"
输出,如预期:
One
This is two
Three
-n
foo bar # tabulation saved
实际上,我不确定,但可能会像以前那样在较老的bash中这样做:
function qs_parse() {
local i=0
while IFS='' read -r line || [[ -n "$line" ]]; do
parsed_str[i]="${line}"
let i++
done < <( printf "%s\n" "$1"|xargs -n 1 printf "%s\n" )
}
tab=' ' # tabulation here
qs_parse "One 'This is two' Three -n 'foo${tab}bar'"
printf "%s\n" "${parsed_str[0]}"
printf "%s\n" "${parsed_str[1]}"
printf "%s\n" "${parsed_str[2]}"
printf "%s\n" "${parsed_str[3]}"
printf "%s\n" "${parsed_str[4]}"
答案 2 :(得分:2)
此问题的解决方案是使用xargs(eval free) 它将双引号字符串保留在一起:
$ test='One "This is two" Three'
$ IFS=$'\n' arr=( $(xargs -n1 <<<"$test") )
$ printf '<%s>\n' "${arr[@]}"
<One>
<This is two>
<Three>
当然,您可以使用该数组设置位置参数:
$ set -- "${arr[@]}"
$ echo "$2"
This is two
答案 3 :(得分:1)
我写了几个本机bash函数来执行此操作:https://github.com/mblais/bash_ParseFields
您可以使用ParseFields
这样的功能:
$ str='field1 field\ 2 "field 3"'
$ ParseFields -d "$str" a b c d
$ printf "|%s|\n|%s|\n|%s|\n|%s|\n" "$a" "$b" "$c" "$d"
|field1|
|field 2|
|field 3|
||
ParseFields的-d
选项删除任何周围的引号并解释已解析字段的反斜杠。
还有一个更简单的ParseField
函数(由ParseFields
使用),用于解析字符串中特定偏移量的单个字段。
请注意,这些函数无法解析流,只能解析字符串。除了空格外,IFS变量还可用于指定字段分隔符。
如果您要求未加引号的撇号可能出现在未加引号字段中,则需要稍作更改 - 请告知我。
答案 4 :(得分:0)
test='One "This is two" Three'
mapfile -t some_args < <(xargs -n1 <<<"$test")
echo "'${some_args[0]}'" "'${some_args[1]}'" "'${some_args[2]}'"
输出: “一个”,“这是两个”,“三个”