是否有一种简洁的方法可以在命令行上通过本地类似 Bourne 的 shell 测试数组支持?
这总是可能的:
$ arr=(0 1 2 3);if [ "${arr[2]}" != 2 ];then echo "No array support";fi
或测试$SHELL
shell 版本:
$ eval $(echo "$SHELL --version") | grep version
然后阅读手册页,假设我有权访问它。 (即使在那里,写自/bin/bash
,我假设所有类似 Bourne 的 shell 都承认长选项--version
,当 ksh 中断时例如.)
我正在寻找一个简单的测试,可以无人值守并合并到用法脚本开头甚至调用之前的部分。
答案1
假设您想要限制为类似 Bourne 的 shell(许多其他 shell,例如、 、 或csh
支持tcsh
数组rc
,但编写同时兼容类似 Bourne 的 shell 的脚本,这些很棘手并且通常毫无意义,因为它们是完全不同的解释器不兼容的语言),请注意实现之间存在显着差异。es
fish
支持数组的类似 Bourne 的 shell(按添加支持的时间顺序排列)是:
ksh88
(原始 ksh 的最后一次演变,第一个实现数组的 ksh88 仍然存在于ksh
大多数传统商业 Unices 上,它也是 的基础sh
)- 数组是一维的
- 如果您不能保证数组不会以or开头,则将数组定义为
set -A array foo bar
or 。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
和衍生品(这是一些 BSD 的基础,ksh
有时也是sh
几个 BSD 的基础,并且是 ksh93 源代码被释放之前唯一的开源 ksh 实现):主要喜欢
ksh88
但请注意:- 一些旧的实现不支持
set -A array -- foo bar
, (--
那里不需要)。 ${#a[@]}
是最大分配索引的索引加一。 (a[1000]=1; echo "${#a[@]}"
即使数组只有一个元素,也会输出 1001。- 在较新的版本中,数组大小不再受到限制(除了整数的大小)。
- 的最新版本
mksh
有一些额外的运算符,其灵感来自于bash
,ksh93
或zsh
类似赋值 laa=(x y)
,a+=(z)
,${!a[@]}
以获取分配的索引列表。
- 一些旧的实现不支持
zsh
。数组通常设计得更好,并且充分利用了数组zsh
的优点。正如你可以看到的ksh
csh
1991 年发布 zsh 2.0,设计灵感来自于tcsh而不是ksh。它们与ksh
数组有一些相似之处,但也有显着差异:- 索引从 1 开始,而不是从 0 开始(模拟除外
ksh
),这与 Bourne 数组(位置参数 $@,也zsh
公开为其 $argv 数组)和csh
数组一致。 - 它们是与普通/标量变量不同的类型。运算符对它们的应用不同,就像您通常期望的那样。
$a
与数组不同,${a[0]}
但扩展到数组的非空元素("${a[@]}"
对于像 in 那样的所有元素ksh
)。 - 它们是普通数组,而不是稀疏数组。
a[5]=1
有效,但如果未分配,则将 1 到 4 的所有元素分配为空字符串。所以${#a[@]}
(与ksh中索引0的元素的大小相同${#a}
)是数组中的元素数量和最大的指定索引。 - 支持关联数组。
- 支持大量用于处理数组的运算符,数量太多,无法在此列出。
- 数组定义为
a=(x y)
.set -A a x y
也适用于与 的兼容性ksh
,但除非在 ksh 仿真中( zsh 仿真中不需要),set -A a -- x y
否则不受支持。--
- 索引从 1 开始,而不是从 0 开始(模拟除外
ksh93
。 (此处描述最新版本)。 ,由原作者ksh93
重写,长期以来一直被认为ksh
实验性的由于它已作为 FOSS 发布,现在可以在越来越多的系统中找到。例如,它是/bin/sh
(它取代了 Bourne shell,/usr/xpg4/bin/sh
POSIX shell 仍然基于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)
一次定义稀疏数组。 - 最大数组索引提高到 4,194,303。
- 虽然没有达到 的程度
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")
将标量变量重新定义为数组,然后才能添加或修改元素。 "$array"
按原样扩展到数组的所有元素,这使得它们比在其他 shell 中更容易使用(与 ksh/bash/zsh相比,以数组的元素作为参数cmd "$array"
进行调用;是关闭的,但剥离了空元素)。cmd
cmd "${array[@]}"
zsh
cmd $array
- 作为 调用时不支持数组
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
) 来获取数组的元素数量 (in $REPLY
),将数组设置为一个整体或分配单个元素。
可能不值得付出努力。我会使用perl
或限制 Bourne/POSIX shell 数组:"$@"
。
如果目的是让用户的交互式 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 方式使用他/她的数组。
答案2
您可以使用eval
尝试数组语法:
is_array_support() (
eval 'a=(1)'
) >/dev/null 2>&1
if is_array_support; then
echo support
else
echo not
fi