Bash 条目包含空格的数组扩展
Bash Array expansion with entries containing whitespaces
将包含白色space的数组传递给另一个函数时出现奇怪的问题。当有问题的数组随后从目标函数的上下文中展开时会发生错误,似乎 whitespaces 被错误地解释为分隔符,因此:
- 不保留每个数组条目中每个单词之间的空格。
- 前面直接有白色的每个单词space,在结果数组中占据一个单独的索引。
如何确保传递给函数的数组在后续扩展后保留其条目中的任何 space?
可以使用以下代码重现错误:
function caller(){
#string to convert to array.
str="var1=val1,var2=val2,var3=value\ with\ a\ space"
#convert argument string to full name value pairs array
fullNameValPairsArr=($(toNameValuePairsArray "$str"))
callee fullNameValPairsArr
}
#called by the top-level "caller" function and passed the array as an argument
function callee(){
passedArray=[@]
expandedPassedArray=("${!passedArray}")
for curEntry in "${expandedPassedArray[@]}"
do
echo $curEntry
done
}
#helper function converts arg string to name value pairs array.
function toNameValuePairsArray(){
#store args string to local
fullNameValPairsString=""
#first split the full name-value pair parameters(via comma delimiter).
IFS=','
#Read the split words into an array based on space delimiter
read -a nameValPairsArr <<< "$fullNameValPairsString"
echo "${nameValPairsArr[@]}"
}
caller
调用顶级“caller”函数的输出是:
var1=val1
var2=val2
var3=value
with
a
space
如您所见,包含 space 的最后一个参数(即带有 space 的值)被拆分为数组中的几个单独条目;字符串中的 space 未保留。
这是您目前正在做的事情:
- 用
read -a
小心地将字符串拆分成正确的数组
- 立即用
echo
连接所有带空格的元素
- 用分词的方式把所有的元素都放在空格上
如果您只是跳过第 2 步和第 3 步,就不会出现此问题。
没有从 bash 函数中获取 return 值的好方法,但是您可以,例如使用全局变量:
function caller(){
#string to convert to array.
str="var1=val1,var2=val2,var3=value\ with\ a\ space"
#convert argument string to full name value pairs array
toNameValuePairsArray "$str"
# Copy from the global variable to an array of our choice
fullNameValPairsArr=( "${toNameValuePairsArray_result[@]}" )
callee fullNameValPairsArr
}
#called by the top-level "caller" function and passed the array as an argument
function callee(){
passedArray=[@]
expandedPassedArray=("${!passedArray}")
for curEntry in "${expandedPassedArray[@]}"
do
echo $curEntry
done
}
#helper function converts arg string to name value pairs array.
function toNameValuePairsArray(){
#store args string to local
fullNameValPairsString=""
#first split the full name-value pair parameters(via comma delimiter).
IFS=','
# "return" by assigning to a global variable
read -a toNameValuePairsArray_result <<< "$fullNameValPairsString"
}
caller
这导致:
var1=val1
var2=val2
var3=value with a space
这是您的主要错误:
fullNameValPairsArr=($(toNameValuePairsArray "$str"))
因为命令替换未加引号,shell 将对输出执行分词,将带空格的参数分成单独的词。
我会使用允许您将变量 names 传递给函数的“namerefs”。
需要 bash 版本 4.3+。
请参阅手册中的 Shell Parameters
if declare -n a=b 2>/dev/null; then
unset a
else
echo "This bash version ($BASH_VERSION) does not implement namerefs." >&2
exit 1
fi
caller() {
local str="var1=val1,var2=val2,var3=value\ with\ a\ space"
local -a fullNameValPairsArr
toNameValuePairsArray "$str" fullNameValPairsArr
callee fullNameValPairsArr
}
callee() {
local -n passedArray=
for curEntry in "${passedArray[@]}"; do
echo "$curEntry"
done
}
toNameValuePairsArray() {
local fullNameValPairsString=""
local -n _ary= # cannot use same varname as caller
# don't set *global* IFS
IFS=',' read -a _ary <<< "$fullNameValPairsString"
}
caller
产出
var1=val1
var2=val2
var3=value with a space
我会更进一步,将输入解析为 关联 数组:
# check bash version...
caller() {
local str="var1=val1,var2=val2,var3=value\ with\ a\ space"
#local -a fullNameValPairsArr
local -A fullNameValPairsArr
toNameValuePairsArray "$str" fullNameValPairsArr
callee fullNameValPairsArr
}
callee() {
local -n passedArray=
for idx in "${!passedArray[@]}"; do
echo "$idx => ${passedArray[$idx]}"
done
}
toNameValuePairsArray() {
local fullNameValPairsString=""
local -n _ary= # cannot use same varname as caller
# don't set *global* IFS
IFS=',' read -a pairs <<< "$fullNameValPairsString"
for pair in "${pairs[@]}"; do
IFS="=" read var value <<<"$pair"
_ary[$var]=$value
done
}
caller
产出
var1 => val1
var2 => val2
var3 => value with a space
最后一点,bash 5.1 提供了一个新的可加载 csv
命令
BASH_LOADABLES_PATH="${BASH%/bin/bash}/lib/bash"
enable -f csv csv
str="var1=val1,var2=val2,var3=value\ with\ a\ space"
csv -a pairs "$str"
declare -p pairs
结果
declare -a pairs=([0]="var1=val1" [1]="var2=val2" [2]="var3=value\ with\ a\ space")
文档:
$ help csv
csv: csv [-a ARRAY] string
Read comma-separated fields from a string.
Parse STRING, a line of comma-separated values, into individual fields,
and store them into the indexed array ARRAYNAME starting at index 0.
If ARRAYNAME is not supplied, "CSV" is the default array name.
将包含白色space的数组传递给另一个函数时出现奇怪的问题。当有问题的数组随后从目标函数的上下文中展开时会发生错误,似乎 whitespaces 被错误地解释为分隔符,因此:
- 不保留每个数组条目中每个单词之间的空格。
- 前面直接有白色的每个单词space,在结果数组中占据一个单独的索引。
如何确保传递给函数的数组在后续扩展后保留其条目中的任何 space?
可以使用以下代码重现错误:
function caller(){
#string to convert to array.
str="var1=val1,var2=val2,var3=value\ with\ a\ space"
#convert argument string to full name value pairs array
fullNameValPairsArr=($(toNameValuePairsArray "$str"))
callee fullNameValPairsArr
}
#called by the top-level "caller" function and passed the array as an argument
function callee(){
passedArray=[@]
expandedPassedArray=("${!passedArray}")
for curEntry in "${expandedPassedArray[@]}"
do
echo $curEntry
done
}
#helper function converts arg string to name value pairs array.
function toNameValuePairsArray(){
#store args string to local
fullNameValPairsString=""
#first split the full name-value pair parameters(via comma delimiter).
IFS=','
#Read the split words into an array based on space delimiter
read -a nameValPairsArr <<< "$fullNameValPairsString"
echo "${nameValPairsArr[@]}"
}
caller
调用顶级“caller”函数的输出是:
var1=val1
var2=val2
var3=value
with
a
space
如您所见,包含 space 的最后一个参数(即带有 space 的值)被拆分为数组中的几个单独条目;字符串中的 space 未保留。
这是您目前正在做的事情:
- 用
read -a
小心地将字符串拆分成正确的数组
- 立即用
echo
连接所有带空格的元素
- 用分词的方式把所有的元素都放在空格上
如果您只是跳过第 2 步和第 3 步,就不会出现此问题。
没有从 bash 函数中获取 return 值的好方法,但是您可以,例如使用全局变量:
function caller(){
#string to convert to array.
str="var1=val1,var2=val2,var3=value\ with\ a\ space"
#convert argument string to full name value pairs array
toNameValuePairsArray "$str"
# Copy from the global variable to an array of our choice
fullNameValPairsArr=( "${toNameValuePairsArray_result[@]}" )
callee fullNameValPairsArr
}
#called by the top-level "caller" function and passed the array as an argument
function callee(){
passedArray=[@]
expandedPassedArray=("${!passedArray}")
for curEntry in "${expandedPassedArray[@]}"
do
echo $curEntry
done
}
#helper function converts arg string to name value pairs array.
function toNameValuePairsArray(){
#store args string to local
fullNameValPairsString=""
#first split the full name-value pair parameters(via comma delimiter).
IFS=','
# "return" by assigning to a global variable
read -a toNameValuePairsArray_result <<< "$fullNameValPairsString"
}
caller
这导致:
var1=val1
var2=val2
var3=value with a space
这是您的主要错误:
fullNameValPairsArr=($(toNameValuePairsArray "$str"))
因为命令替换未加引号,shell 将对输出执行分词,将带空格的参数分成单独的词。
我会使用允许您将变量 names 传递给函数的“namerefs”。
需要 bash 版本 4.3+。
请参阅手册中的 Shell Parameters
if declare -n a=b 2>/dev/null; then
unset a
else
echo "This bash version ($BASH_VERSION) does not implement namerefs." >&2
exit 1
fi
caller() {
local str="var1=val1,var2=val2,var3=value\ with\ a\ space"
local -a fullNameValPairsArr
toNameValuePairsArray "$str" fullNameValPairsArr
callee fullNameValPairsArr
}
callee() {
local -n passedArray=
for curEntry in "${passedArray[@]}"; do
echo "$curEntry"
done
}
toNameValuePairsArray() {
local fullNameValPairsString=""
local -n _ary= # cannot use same varname as caller
# don't set *global* IFS
IFS=',' read -a _ary <<< "$fullNameValPairsString"
}
caller
产出
var1=val1
var2=val2
var3=value with a space
我会更进一步,将输入解析为 关联 数组:
# check bash version...
caller() {
local str="var1=val1,var2=val2,var3=value\ with\ a\ space"
#local -a fullNameValPairsArr
local -A fullNameValPairsArr
toNameValuePairsArray "$str" fullNameValPairsArr
callee fullNameValPairsArr
}
callee() {
local -n passedArray=
for idx in "${!passedArray[@]}"; do
echo "$idx => ${passedArray[$idx]}"
done
}
toNameValuePairsArray() {
local fullNameValPairsString=""
local -n _ary= # cannot use same varname as caller
# don't set *global* IFS
IFS=',' read -a pairs <<< "$fullNameValPairsString"
for pair in "${pairs[@]}"; do
IFS="=" read var value <<<"$pair"
_ary[$var]=$value
done
}
caller
产出
var1 => val1
var2 => val2
var3 => value with a space
最后一点,bash 5.1 提供了一个新的可加载 csv
命令
BASH_LOADABLES_PATH="${BASH%/bin/bash}/lib/bash"
enable -f csv csv
str="var1=val1,var2=val2,var3=value\ with\ a\ space"
csv -a pairs "$str"
declare -p pairs
结果
declare -a pairs=([0]="var1=val1" [1]="var2=val2" [2]="var3=value\ with\ a\ space")
文档:
$ help csv
csv: csv [-a ARRAY] string
Read comma-separated fields from a string.
Parse STRING, a line of comma-separated values, into individual fields,
and store them into the indexed array ARRAYNAME starting at index 0.
If ARRAYNAME is not supplied, "CSV" is the default array name.