通過 shell 測試對數組的支持
是否有一種簡潔的方法可以在命令行中通過本地 Bourne-like shell 測試陣列支持?
這總是可能的:
$ arr=(0 1 2 3);if [ "${arr[2]}" != 2 ];then echo "No array support";fi
或測試
$SHELL
和外殼版本:$ eval $(echo "$SHELL --version") | grep version
然後閱讀手冊頁,假設我可以訪問它。(即使在那裡,從 寫作
/bin/bash
,我假設所有類似 Bourne 的 shell 都承認 long 選項,例如--version
,當 ksh 中斷時。)我正在尋找一個簡單的測試,它可以在腳本開始時甚至在呼叫它之前無人看管並合併到使用部分中。
假設您想限制為類 Bourne 的 shell(許多其他 shell,如
csh
、tcsh
、或支持數組rc
,但同時編寫一個與類 Bourne 的 shell 兼容的腳本,這些是棘手且通常毫無意義的,因為它們是完全不同的解釋器和不兼容的語言),請注意實現之間存在顯著差異。es``fish
支持數組的 Bourne like shell(按添加支持的時間順序)是:
ksh88
(原始 ksh 的最後一次演變,第一個實現數組,ksh88 仍然存在ksh
於大多數傳統的商業 Unices 上,它也是 的基礎sh
)
- 數組是一維的
- 數組被定義為
set -A array foo bar
orset -A array -- "$var" ...
如果你不能保證它$var
不會以 a-
or開頭+
。- 數組索引從
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
類似賦值 a 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 的所有元素,則將其分配為空字元串。因此(與ksh 中的${#a[@]}
相同是索引 0 的元素的大小)是數組中的元素數和分配的最大索引。${#a}
- 支持關聯數組。
- 支持大量使用數組的運算符,此處無法列出。
- 數組定義為
a=(x y)
.set -A a x y
也適用於兼容ksh
,但set -A a -- x y
不支持,除非在 ksh 仿真中(--
在 zsh 仿真中不需要)。
ksh93
. (此處描述最新版本)。ksh93
,ksh
由原作者重寫,長期以來被認為是實驗性的,現在可以在越來越多的系統中找到它,因為它已作為 FOSS 發布。例如,它是/bin/sh
(它取代了 Bourne shell,/usr/xpg4/bin/sh
POSIX shell 仍然基於ksh88
)和ksh
ofSolaris 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"
呼叫; ‘s接近但剝離空元素)。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
for 數組和其他幾個領域的兼容性):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 方式使用他/她的數組的函式中。