在sh shell(而非bash)下使用数组

5

一个简单的问题,我能在只使用sh,而不是bashzsh或其他任何东西的情况下使用数组吗?

ARRAYNAME=(value1 value2 .... valueN)
上面的命令只在bash中有效。有没有在sh中的替代方法
我尝试搜索这样的问题,但没有找到适用于纯sh的答案。

1个回答

12
在POSIX sh中,每个堆栈帧只有一个"array",即当前作用域的参数列表。您可以对其进行重置:
set -- "first item" "second item"

...追加到它后面:

set -- "$@" "new item"

...从其前面删除内容:

echo "First item is $1"
shift
echo "First item is $1"

...并通过进入新的函数作用域来创建一个新的作用域:

someFunc() {
  echo "The argument list for this function is:" >&2
  printf ' - %s\n' "$@"
}

someFunc argOne argTwo

......但由于一次只有一个在范围内,它非常有限。

如果它不受限制,那么就没有必要添加任何其他形式的数组(比如ksh和后来的bash等)!


一种hack方法是滥用字符串,将其视为数组,在需要索引时将它们拆分为函数参数:

s='A|B|C|D' # specify your "array" as a string with a sigil between elements
IFS='|'     # specify separator between elements
set -f      # disable glob expansion, so a * doesn't get replaced with a list of files

getNth()  { shift "$(( $1 + 1 ))"; printf '%s\n' "$1"; }
getLast() { getNth "$(( $(length "$@") - 1 ))" "$@"; }
length()  { echo "$#"; }

length $s   # emits 4
getNth 0 $s # emits A
getNth 1 $s # emits B
getLast $s  # emits D

当然,这意味着您需要有一个保留的特殊字符,而该字符不在值中出现。
作为第三方shell库,其操作方式类似于上述过程(将“数组”编码为字符串,并在这些字符串中存储和检索内容),但具有转义/取消转义支持,因此不需要特殊字符。您还可以查看https://github.com/makefu/array/blob/master/array。尽管如此,以上代码对于每个查找都会调用外部可执行文件--通过使用POSIX sh来调用sedawk的副本,您很快就会失去在shell启动时间上获得的任何优势。

1
另外,添加前缀:set -- new "$@" - glenn jackman
posix sh(或bash)是否为每个函数调用创建堆栈帧?如果每个函数调用都有自己的堆栈帧,动态作用域如何成为可能(并且默认情况下)? - hek2mgl
@hek2mgl,嗯?就像在C语言中同时有堆和栈一样,在bash或ksh中(其中一个本地变量),你的本地变量存在于栈中,并且其他变量则存在于栈外。我并不是要将“栈帧”这个词组理解得过于狭隘,但是bash确实在其自己的文档中广泛使用这个术语(例如,请参见BASH_ARGCBASH_ARGV的定义;同样,FUNCNAME的定义)。即使在POSIX sh中,对于参数列表,栈也是明显的实现方式,也是用于描述行为的简单习惯用法。 - Charles Duffy
@hek2mgl,...阅读dash源代码时,它在eval.c文件的evalfun函数中使用C栈来存储参数列表的旧值,当跳入一个函数时--但实际上它仍然是一个堆栈。 - Charles Duffy
@CharlesDuffy 感谢您的回复。我发表评论是希望学习,而不是为了证明自己是对的 :) 我还没有深入研究 C 代码层面,最近只是在调试应用程序时了解了 shell 中的动态作用域。在阅读了那段代码后,我个人会避免使用它。 - hek2mgl

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接