将多个数组作为参数传递给Bash脚本?

时间:2017-04-28 18:38:23

标签: arrays bash shell command-line

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

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

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

这样:var1=array1var2=array2以及var3=array3

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

4 个答案:

答案 0 :(得分:7)

shell将一个参数向量(也就是说,一个简单的C字符串数组)传递给正在运行的程序。这是操作系统级别的限制:不存在方法来在参数列表中的两个程序(任何两个程序,用任何语言编写!)之间传递结构化数据,除非在内容中编码该结构这个C字符串数组的成员。

方法:长度前缀

如果效率是目标(在解析的简易性和命令行和环境存储的ARG_MAX限制之外使用的空间量方面),要考虑的一种方法是为每个数组添加前缀描述其长度的论据。

但是,通过提供长度参数,您可以指示该参数列表的哪些部分应该是给定数组的一部分:

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

...然后,在脚本中,您可以使用length参数将内容拆分回数组:

#!/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-Delimited Streams

另一种方法是为每个数组传递一个文件名,从中可以读取NUL分隔的内容列表。这种方法的一个主要优点是数组内容的大小不计入操作系统强制命令行长度限制ARG_MAX。此外,对于可用的操作系统,下面不会创建真正的磁盘文件,而是创建/dev/fd - 由写入每个数组内容的子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"

答案 1 :(得分:0)

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]}");

编辑:由于Charles简化了他的解决方案,它可能是一个比我更好,更清晰的解决方案。

答案 2 :(得分:0)

这是一个代码示例,该示例演示如何将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

工作完成:-)

答案 3 :(得分:-1)

根据此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[@]"

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