【问题标题】:Pass multiple arrays as arguments to a Bash script?将多个数组作为参数传递给 Bash 脚本?
【发布时间】:2017-09-26 23:11:26
【问题描述】:

我查看过,但只看到脚本中传递的一个数组的答案。

我想将多个数组传递给一个 bash 脚本,该脚本将它们分配为单独的变量,如下所示:

./myScript.sh ${array1[@]} ${array2[@]} ${array3[@]}

这样:var1=array1var2=array2var3=array3

我尝试了多个选项,但 variableName=("$@") 将所有数组组合到每个变量中。我希望在我的 bash 脚本中有一个代表每个数组的变量。

【问题讨论】:

  • 数组是否包含任意数据,或者是否存在您知道不会出现在数据中的字符?例如,如果您的数组不包含逗号之类的内容,我可以在几分钟内找到解决方案
  • Bash 变量很简单,不能有这样的嵌套结构。
  • @Barmar 你可以用一点技巧
  • 命令行传递的参数是字符串。如果你想强加一个结构,你需要自己序列化它。 (例如,传入 JSON)
  • (...顺便说一句——我倾向于建议反对使用.sh扩展名。可执行脚本定义命令,而命令通常不有扩展:一个不运行ls.elfsystriage.py;如果用不同的语言重写命令而不更新所有调用者,扩展可能会产生误导;此外,暗示 POSIX sh 合规性的扩展可能会导致人们调用脚本需要 ksh 或 bash 扩展才能使用不提供相同功能的解释器正确运行)。

标签: arrays bash shell command-line


【解决方案1】:

这是一个代码示例,它展示了如何将 2 个数组传递给一个函数。除了提供完整的代码示例之外,没有什么比以前的答案更多了。

这是在bash 4.4.12 中编码的,即在bash 4.3 之后,这需要不同的编码方法。一个数组包含要着色的文本,另一个数组包含要用于每个文本元素的颜色:

function cecho_multitext () {
  # usage : cecho_multitext message_array color_array
  # what it does : Multiple Colored-echo.

    local -n array_msgs=$1
    local -n array_colors=$2
    # printf '1: %q\n' "${array_msgs[@]}"
    # printf '2: %q\n' "${array_colors[@]}"


    local i=0
    local coloredstring=""
    local normalcoloredstring=""

    # check array counts
    # echo "msg size : "${#array_msgs[@]}
    # echo "col size : "${#array_colors[@]}

    [[ "${#array_msgs[@]}" -ne "${#array_colors[@]}" ]] && exit 2

    # build the colored string
    for msg in "${array_msgs[@]}"
    do
      color=${array_colors[$i]}
      coloredstring="$coloredstring $color $msg "
      normalcoloredstring="$normalcoloredstring $msg"
      # echo -e "coloredstring ($i): $coloredstring"
      i=$((i+1))
    done

    # DEBUG
    # echo -e "colored string : $coloredstring"
    # echo -e "normal color string : $normal $normalcoloredstring"

    # use either echo or printf as follows :
    # echo -e "$coloredstring"
    printf '%b\n' "${coloredstring}"

    return
}

调用函数:

#!/bin/bash
green='\E[32m'
cyan='\E[36m'
white='\E[37m'
normal=$(tput sgr0)
declare -a text=("one" "two" "three" )
declare -a color=("$white" "$green" "$cyan")
cecho_multitext text color

工作完成:-)

【讨论】:

    【解决方案2】:

    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 的用户)的一种常见做法是允许多次传递参数以修改给定数组。在 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 样式的指向FIFO 的链接,这些链接由写入每个数组内容的子shell 写入。

    ./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"
    

    【讨论】:

    • 在第二种情况下,我想你会想要"${1#*=}" 或类似的。
    • 这是一个放在旧 bash 工具箱中的守护者,在作为参数传递时组合数组之后。干得好。
    【解决方案3】:

    根据question 的答案,您可以尝试以下方法。

    将数组定义为 shell 上的变量:

    array1=(1 2 3)
    array2=(3 4 5)
    array3=(6 7 8)
    

    有一个这样的脚本:

    arg1=("${!1}")
    arg2=("${!2}")
    arg3=("${!3}")
    
    
    echo "arg1 array=${arg1[@]}"
    echo "arg1 #elem=${#arg1[@]}"
    
    echo "arg2 array=${arg2[@]}"
    echo "arg2 #elem=${#arg2[@]}"
    
    echo "arg3 array=${arg3[@]}"
    echo "arg3 #elem=${#arg3[@]}"
    

    然后这样称呼它:

    . ./test.sh "array1[@]" "array2[@]" "array3[@]"
    

    请注意,脚本需要获取源(. 或 source),以便在当前 shell 环境而不是子 shell 中执行。

    【讨论】:

    • 嗯?基本 shell 变量可以导出到环境中,因此可用于子进程,但数组不能。预计这将如何运作?
    • 这个方法适用于函数,但我很确定@CharlesDuffy 是对的
    • 查尔斯你是对的。但是,正如 anubhava 答案中的链接问题中所解释的那样,诀窍在于获取(。或源)脚本,以便脚本在当前 shell 环境中执行,而不是在子 shell 中执行。
    • @CharlesDuffy 是的,你又是对的。为了清楚起见,我将编辑答案并将其删除。
    【解决方案4】:

    Charles Duffy 的响应效果非常好,但我会采用不同的方式,以便更简单地在脚本中初始化 var1var2var3

    ./myScript.sh "${#array1[@]} ${#array2[@]} ${#array3[@]}" \
     "${array1[@]}" "${array2[@]}" "${array3[@]}"
    

    然后在myScript.sh

    #!/bin/bash
    declare -ai lens=($1);
    declare -a var1=("${@:2:lens[0]}") var2=("${@:2+lens[0]:lens[1]}") var3=("${@:2+lens[0]+lens[1]:lens[2]}");
    

    编辑:由于查尔斯简化了他的解决方案,它可能比我的解决方案更好、更清晰。

    【讨论】:

      猜你喜欢
      • 2023-04-03
      • 2020-03-24
      • 2013-06-18
      • 2021-11-04
      • 1970-01-01
      • 2021-11-18
      • 2012-12-29
      • 1970-01-01
      相关资源
      最近更新 更多