shell 将单个参数向量(也就是说,一个简单的 C 字符串数组)传递给正在运行的程序。这是操作系统级别的限制:有不存在任何方法在参数列表中的两个程序(任何两个程序,用任何语言编写!)之间传递结构化数据,除非将该结构编码在此 C 字符串数组的成员内容中。
方法:长度前缀
如果效率是一个目标(无论是在易于解析还是在使用的空间量方面)ARG_MAX
命令行和环境存储的限制),一种可以考虑的方法是为每个数组添加一个描述其长度的参数作为前缀。
但是,通过提供长度参数,您可以指示该参数列表的哪些部分应该是给定数组的一部分:
./myScript \
"${#array1[@]}" "${array1[@]}" \
"${#array2[@]}" "${array2[@]}" \
"${#array3[@]}" "${array3[@]}"
...然后,在脚本内,您可以使用长度参数将内容拆分回数组:
#!/usr/bin/env bash
array1=( "${@:2:$1}" ); shift "$(( $1 + 1 ))"
array2=( "${@:2:$1}" ); shift "$(( $1 + 1 ))"
array3=( "${@:2:$1}" ); shift "$(( $1 + 1 ))"
declare -p array1 array2 array3
如果运行为./myScript 3 a b c 2 X Y 1 z
,其输出为:
declare -a array1='([0]="a" [1]="b" [2]="c")'
declare -a array2='([0]="X" [1]="Y")'
declare -a array3='([0]="z")'
方法:每个参数数组名称前缀
顺便说一句,Python 世界中常见的做法(特别是对于使用argparse library https://docs.python.org/2/library/argparse.html) 是允许多次传递参数以修改给定数组。在 shell 中,这看起来像:
./myScript \
"${array1[@]/#/--array1=}" \
"${array2[@]/#/--array2=}" \
"${array3[@]/#/--array3=}"
然后解析它的代码可能如下所示:
#!/usr/bin/env bash
declare -a args array1 array2 array3
while (( $# )); do
case $1 in
--array1=*) array1+=( "${1#*=}" );;
--array2=*) array2+=( "${1#*=}" );;
--array3=*) array3+=( "${1#*=}" );;
*) args+=( "$1" );;
esac
shift
done
因此,如果你的原始值是array1=( one two three ) array2=( aye bee ) array3=( "hello world" )
,调用约定为:
./myScript --array1=one --array1=two --array1=three \
--array2=aye --array2=bee \
--array3="hello world"
方法:NUL 分隔流
另一种方法是为每个数组传递一个文件名,从中可以读取以 NUL 分隔的内容列表。这种方法的一个主要优点是数组内容的大小不计入ARG_MAX
,操作系统强制执行的命令行长度限制。此外,对于可用的操作系统,下面的内容不会创建真正的磁盘文件,而是创建/dev/fd
-style 链接到由写入每个数组内容的子 shell 写入的 FIFO。
./myScript \
<( (( ${#array1[@]} )) && printf '%s\0' "${array1[@]}") \
<( (( ${#array2[@]} )) && printf '%s\0' "${array2[@]}") \
<( (( ${#array3[@]} )) && printf '%s\0' "${array3[@]}")
...并且,阅读(使用 bash 4.4 或更高版本,提供mapfile -d
):
#!/usr/bin/env bash
mapfile -d '' array1 <"$1"
mapfile -d '' array2 <"$2"
mapfile -d '' array3 <"$3"
...或者,支持较旧的 bash 版本:
#!/usr/bin/env bash
declare -a array1 array2 array3
while IFS= read -r -d '' entry; do array1+=( "$entry" ); done <"$1"
while IFS= read -r -d '' entry; do array2+=( "$entry" ); done <"$2"
while IFS= read -r -d '' entry; do array3+=( "$entry" ); done <"$3"