我们在数组一章中介绍了数组。本文会展示更多使用数组的方法。
在上一章数组章节中,你看到了带有 @ 下标的 "${myarray[@]}",它用于把数组展开成各个独立的元素。
但有时你想把所有元素连接成一个字符串。
这时可以使用 * 下标:
echo "${myarray[*]}"
展开式必须用双引号括起来。
Bash 使用内置变量 IFS 的第一个字符作为分隔字符。
默认情况下,$IFS 由空格、制表符和换行符组成。
myarray=(one two three four)
mystring="${myarray[*]}"
declare -p mystring
# => declare -- mystring="one two three four"
我们可以修改 IFS 变量,改用不同的分隔字符:
myarray=(one two three four)
IFS=","
mystring="${myarray[*]}"
declare -p mystring
# => declare -- mystring="one,two,three,four"
join() {
local IFS=$1
shift
local elements=("$@")
echo "${elements[*]}"
}
join ":" "${myarray[@]}" # note, the "@" index
# => "one:two:three:four"
在函数内把 IFS 局部化,意味着我们不必保存旧值,再把它恢复成全局作用域中的原值。
更精妙的是,特殊参数 "$*" 加上引号后就具有相同的功能,因此我们不需要保存一份函数实参的副本:
join() {
local IFS=$1
shift
echo "$*"
}
(IFS=","; echo "${myarray[*]}")
括号会创建一个子 shell(当前 shell 的副本)。括号内的命令执行完毕后,子 shell 退出,被修改的 IFS 变量也随之消失。
注意,这样写不起作用:IFS="," echo "${myarray[*]}",参数展开会先执行,之后 shell 才会把修改后的 IFS 变量应用到 echo 命令上。
你可能见过 "${variable:offset:length}" 这种参数展开,它会展开成变量值中的子字符串。
对数组也可以这样做,从而展开数组的一个切片。
myarray=(one two three four)
subarray=("${myarray[@]:0:2}")
declare -p subarray
# => declare -a subarray=([0]="one" [1]="two")
subarray=("${myarray[@]:1:3}")
declare -p subarray
# => declare -a subarray=([0]="two" [1]="three" [2]="four")
省略长度部分表示“从偏移量到数组末尾”:
subarray=("${myarray[@]:2}")
declare -p subarray
# => declare -a subarray=([0]="three" [1]="four")
这并不像你可能熟悉的其他语言那样直接。 把数组传给函数主要有两种方法。
第一种方法是把数组的所有值都传进去,在函数中把它们收集到一个局部数组里。
myfunc() {
local array_copy=("$@")
# do stuff with array_copy
declare -p array_copy
}
array_original=(11 22 33 44)
myfunc "${array_original[@]}"
函数中的数组保存的是这些值的副本。 在函数里对数组所做的任何修改都不会反映到外层作用域。
这种方法更像你可能从其他语言中了解的“按引用传递”能力。 你以字符串的形式传入数组的名字。 函数会创建一个带有 “nameref” 属性的局部变量。 这个局部数组和全局数组(也就是我们传入名字的那个)正是同一个数组。
myfunc() {
# note the `-n` option
local -n local_array=$1
# do stuff with local_array
for i in "${!local_array[@]}"; do
printf '%d => %s\n' "$i" "${local_array[i]}"
end
# we can mutate it
local_array+=(55 66 77)
}
array_original=(11 22 33 44)
myfunc "array_original"
# show the mutated array
declare -p array_original
# => declare -a array_original=([0]="11" [1]="22" [2]="33" [3]="44" [4]="55" [5]="66" [6]="77")
nameref 也适用于关联数组和“标量”变量(也就是保存字符串值的变量)。
在函数内部,declare -p local_array 帮不上太多忙。
它只会输出 declare -n local_array="array_original"。
你可以通过查看传入的数组名来获取该数组的详细信息:declare -p "$1"
注意让局部数组的名字与传入的数组不同。 代码仍然可以运行,但会发出这样的 “circular name reference” 警告:
myfunc() {
local -n a=$1
local IFS=,
echo "${a[*]}"
}
# same name as the function's local variable
a=(one two three)
myfunc a
bash: local: warning: a: circular name reference
bash: warning: a: circular name reference
bash: warning: a: circular name reference
bash: warning: a: circular name reference
one,two,three
在只力求符合 POSIX 标准的 shell(如 ash 和 dash)中,并没有数组。
最接近的办法就是使用位置参数。
$1、$2 等。"$@" 可以把它们展开成各个独立的元素"$*" 可以把它们拼接成一个字符串$#
使用 set 命令给它们赋值:
set -- one two three
set -- "$@" four
for item in "$@"; do
echo "do something with $item"
done
如果你的目标是编写“可移植”的 shell 脚本,你会用位置参数来保存一组值的“列表”。