假设你要限制对类似Bourne外壳(许多其他炮弹一样csh
,tcsh
,rc
,es
或fish
支持的阵列,但在同一时间编写脚本兼容的Bourne像壳以及那些是棘手的,通常毫无意义的,因为它们是口译完全不同的,不兼容的语言),请注意,实现之间存在重大差异。
支持数组的Bourne类shell是:
ksh88
(这是第一个实现数组,ksh88仍然是ksh
大多数传统的商业Unices上的应用,也是它的基础sh
)
- 数组是一维的
- 数组定义为,
set -A array foo bar
或者set -A array -- "$var" ...
如果您不能保证$var
不会以开头-
或+
。
- 数组索引始于
0
。
- 各个数组元素分配为
a[1]=value
。
- 数组稀疏。这是
a[5]=foo
将工作,即使a[0,1,2,3,4]
没有设置,将使他们未设置。
${a[5]}
访问索引5的元素(如果数组稀疏,则不一定是第6个元素)。的5
可以有任意的算术表达式。
- 数组大小和下标被限制为(4096)。
${#a[@]}
是数组中已分配元素的数量(不是最大已分配索引)。
- 无法知道分配的下标列表(除了使用单独测试4096个元素之外
[[ -n "${a[i]+set}" ]]
)。
$a
与相同${a[0]}
。那就是数组通过给标量变量额外的值来扩展它们。
pdksh
和衍生产品(这是 ksh
sh
是多个BSD,有时是多个BSD并且是在释放ksh93源之前唯一的开源ksh实现):
通常喜欢,ksh88
但请注意:
- 一些旧的实现不支持
set -A array -- foo bar
,(--
那里并不需要)。
${#a[@]}
是最大分配的索引中的一个。(a[1000]=1; echo "${#a[@]}"
即使阵列只有一个元素,也会输出1001。
- 在较新的版本中,数组大小不再受限制(除了整数大小)。
- 最近的版本
mksh
具有启发了一些额外的运营商bash
,ksh93
或zsh
像分配一拉a=(x y)
,a+=(z)
,${!a[@]}
获得分配的索引列表。
zsh
。zsh
数组通常设计得更好,并会充分利用ksh
and csh
数组。它们类似于ksh
但有显着差异:
- 索引从1开始,而不是0(在
ksh
仿真中除外),这与Bourne数组(位置参数$ @,zsh
也作为其$ argv数组公开)和csh
数组一致。
- 它们是与普通/标量变量不同的类型。运营商对他们的应用方式有所不同,就像您通常期望的那样。与数组
$a
不同,${a[0]}
但扩展为数组的非空元素("${a[@]}"
对于in中的所有元素ksh
)。
- 它们是普通数组,而不是稀疏数组。
a[5]=1
可以,但是如果未分配,则将1到4的所有元素分配为空字符串。因此${#a[@]}
(与${#a}
ksh中indice 0元素的大小相同)是数组中元素的数量和分配的最大indice。
- 支持关联数组。
- 支持使用数组的大量运算符,太大了,无法在此处列出。
- 定义为的数组
a=(x y)
。set -A a x y
也可以使用,但是set -A a -- x y
不支持,除非在ksh仿真中(--
在zsh仿真中不需要)。
ksh93
。(此处介绍最新版本)。ksh93
,由于它已经作为FOSS发布,因此在越来越多的系统中可以找到长期以来被认为是实验性的项目。例如,它的/bin/sh
(它取代Bourne shell的/usr/xpg4/bin/sh
,POSIX的外壳仍然是以ksh88
)和ksh
的Solaris 11
。它的阵列扩展并增强了ksh88。
a=(x y)
可用于定义数组,但由于a=(...)
也用于定义复合变量(a=(foo=bar bar=baz)
),a=()
因此模棱两可,并且声明了复合变量,而不是数组。
- 数组是多维(
a=((0 1) (0 2))
),数组元素也可以是复合变量(a=((a b) (c=d d=f)); echo "${a[1].c}"
)。
- 一种
a=([2]=foo [5]=bar)
语法可用于一次定义稀疏数组。
- 尺寸限制解除。
- 并非达到的程度
zsh
,但也支持大量的运算符来操纵数组。
"${!a[@]}"
检索数组索引列表。
- 关联数组也支持作为单独的类型。
bash
。bash
是GNU项目的外壳。它sh
在最新版本的OS / X和某些GNU / Linux发行版中使用。bash
数组大多模拟ksh88
具有ksh93
和特性的数组zsh
。
a=(x y)
支持的。set -A a x y
不支持。a=()
创建一个空数组(中没有复合变量bash
)。
"${!a[@]}"
用于索引列表。
a=([foo]=bar)
支持的语法以及来自ksh93
和的其他一些语法zsh
。
- 最新
bash
版本还支持将关联数组作为单独的类型。
yash
。这是一个相对较新的,干净的,可识别多字节的POSIX sh实现。没有广泛使用。它的数组是另一个干净的API,类似于zsh
- 数组不稀疏
- 数组索引从1开始
- 定义(并声明)
a=(var value)
- 用
array
内置元素插入,删除或修改的元素
array -s a 5 value
如果未事先分配该元素,则修改第5 个元素将失败。
- 数组中元素的数量为
${a[#]}
,${#a[@]}
是列表中元素的大小。
- 数组是一个单独的类型。您需要
a=("$a")
先将标量变量重新定义为数组,然后才能添加或修改元素。
- 以调用时不支持数组
sh
。
因此,从中您可以看到检测到阵列支持,您可以这样做:
if (unset a; set -A a a; eval "a=(a b)"; eval '[ -n "${a[1]}" ]'
) > /dev/null 2>&1
then
array_supported=true
else
array_supported=false
fi
不足以使用这些数组。您需要定义包装器命令以将数组分配为一个整体和单个元素,并确保您不尝试创建稀疏数组。
喜欢
unset a
array_elements() { eval "REPLY=\"\${#$1[@]}\""; }
if (set -A a -- a) 2> /dev/null; then
set -A a -- a b
case ${a[0]}${a[1]} in
--) set_array() { eval "shift; set -A $1"' "$@"'; }
set_array_element() { eval "$1[1+(\$2)]=\$3"; }
first_indice=0;;
a) set_array() { eval "shift; set -A $1"' -- "$@"'; }
set_array_element() { eval "$1[1+(\$2)]=\$3"; }
first_indice=1;;
--a) set_array() { eval "shift; set -A $1"' "$@"'; }
set_array_element() { eval "$1[\$2]=\$3"; }
first_indice=0;;
ab) set_array() { eval "shift; set -A $1"' -- "$@"'; }
set_array_element() { eval "$1[\$2]=\$3"; }
first_indice=0;;
esac
elif (eval 'a[5]=x') 2> /dev/null; then
set_array() { eval "shift; $1=("'"$@")'; }
set_array_element() { eval "$1[\$2]=\$3"; }
first_indice=0
elif (eval 'a=(x) && array -s a 1 y && [ "${a[1]}" = y ]') 2> /dev/null; then
set_array() { eval "shift; $1=("'"$@")'; }
set_array_element() {
eval "
$1=(\${$1+\"\${$1[@]}"'"})
while [ "$(($2))" -ge "${'"$1"'[#]}" ]; do
array -i "$1" "$2" ""
done'
array -s -- "$1" "$((1+$2))" "$3"
}
array_elements() { eval "REPLY=\${$1[#]}"; }
first_indice=1
else
echo >&2 "Array not supported"
fi
然后你访问与数组元素"${a[$first_indice+n]}"
,整个列表与"${a[@]}"
和使用包装函数(array_elements
,set_array
,set_array_element
),以获得(在一个数组的元素的数量$REPLY
),设置数组作为一个整体或指定单个元素。
可能不值得付出努力。我会使用perl
或限制Bourne / POSIX shell数组:"$@"
。
如果目的是要让用户的交互式外壳程序提供一些文件来定义内部使用数组的函数,那么这里还有一些可能有用的注释。
您可以将zsh
数组配置为更类似于ksh
本地作用域中的数组(在函数或匿名函数中)。
myfunction() {
[ -z "$ZSH_VERSION" ] || setopt localoption ksharrays
# use arrays of indice 0 in this function
}
您还可以使用以下方法仿真ksh
(提高与ksh
数组和其他几个区域的兼容性):
myfunction() {
[ -z "$ZSH_VERSION" ] || emulate -L ksh
# ksh code more likely to work here
}
考虑到这一点,你是愿意放弃的支持yash
和ksh88
与旧版本的pdksh
衍生物,只要你不尝试创建稀疏数组,你应该能够一致地使用:
a[0]=foo
a=(foo bar)
(但不是a=()
)
"${a[#]}"
,"${a[@]}"
,"${a[0]}"
在具有的函数中emulate -L ksh
,而zsh
用户通常仍以zsh方式使用其数组。