【问题标题】:Fill array passed as argument in bash function在 bash 函数中作为参数传递的填充数组
【发布时间】:2014-10-30 05:55:44
【问题描述】:

我需要在函数内部填充一个数组,将其作为参数传递,而不是使用全局变量(该函数将在这里和那里使用不同的数组调用)。

我已阅读此讨论 bash how to pass array as an argument to a function 并使用 pass-by-name 解决方案,几乎可以解决问题。

这是我的代码

#!/bin/bash

function fillArray {
  arrayName=$1[@]
  array=("${!arrayName}")
  for i in 0 1 2 3
  do
    array+=("new item $i")
  done

  echo "Tot items in function: ${#array[@]}"
  for item in "${array[@]}"
  do
    echo $item
  done
  echo
}

myArray=("my item 0" "my item 1")

fillArray myArray

echo "Tot items in main: ${#myArray[@]}"
for item in "${myArray[@]}"
do
  echo $item
done

这是输出

Tot items in function: 6
my item 0
my item 1
new item 0
new item 1
new item 2
new item 3

Tot items in main: 2
my item 0
my item 1

因此,函数正确地使用了作为参数传递的数组(前两项是在主声明中添加的一项),并将新项附加到数组中。函数调用后,在 main 中,添加的项丢失了。

我错过了什么?可能是一些传递引用/传递复制的东西......

谢谢!

【问题讨论】:

    标签: arrays bash function pass-by-reference


    【解决方案1】:

    为了将来的参考,这对于bash 4.3 中的命名引用变得微不足道:

    function fillArray {
      declare -n arrayName=$1
    
      for i in 0 1 2 3
      do
        arrayName+=("new item $i")
      done
    
      echo "Tot items in function: ${#arrayName[@]}"
      for item in "${arrayName[@]}"
      do
        echo $item
      done
      echo
    }
    

    【讨论】:

    • 请注意,许多发行版刚刚开始提供bash 4.2,因此如果您不想自己升级,4.3 可能需要等待很长时间。
    • ksh 拥有 nameref 变量已有一段时间了。很高兴看到 bash 赶上了这个非常有用的功能。
    • 是一个很好的答案,一旦你可以合理地期望有人使用bash 4.3 :)
    • @glennjackman 确实如此。我需要花一些时间了解ksh,如果只是想知道所有bash 都借了什么:)
    【解决方案2】:

    在这个answer 中使用了这种技术。修改后,可以

    addtoarray () { var="$1"; shift 1; eval "$var+=($(printf "'%s' " "$@"))"; }
    
    arr=('my item 0' 'my item 1')
    printf "%s\n" "${arr[@]}"
    echo ====
    addtoarray arr 'my new item 2' 'my new item 3'
    printf "%s\n" "${arr[@]}"
    

    打印

    my item 0
    my item 1
    ====
    my item 0
    my item 1
    my new item 2
    my new item 3
    

    也适用于初始化数组

    addtoarray secarr $(seq 5)
    printf "%s\n" "${secarr[@]}"
    

    打印:

    1
    2
    3
    4
    5
    

    编辑

    函数的分解 - 用作代码生成器

    addtoarray () { var="$1"; shift 1; eval "$var+=($(printf "'%s' " "$@"))"; }
    
    • 函数的第一个参数是变量name(!!!)(不是值)
    • 名称(在 $1 中)存储到 $var 并将其从参数中移出
    • 现在参数只包含数组的新元素,例如val1 val2
    • 现在正在构建一个 bash 命令:
    somearrayname+=('elemnts1' 'element2' .... )
    
    • somearrayname 是我们作为第一个参数传递的变量名
    • printf "'%s '" "$@" 从 arg-list 创建单引号数组成员
    • 所以如果调用函数为addtoarray arr val1 "spaced val2",我们会生成下一个字符串
    arr+=('val1' 'spaced val2' )
    

    这是将成员添加到名为 arr 的数组的正确结构 - 无论是否在 contets 之前,例如将新元素添加到其末尾。 (如果是空的,结束就是它的开始)

    • 终于eval执行上述生成的字符串
    • 在结果中你得到了一个初始化/修改的数组$arr($arr 是全局变量,因此在函数中也是可见的)

    最后 - 残酷地偷走了@chepner 的答案;)使用他的技术addtoarray 很简单:

    addtoarray () {
        declare -n arrname=$1
        shift 1;
        arrname+=("$@")
    }
    

    如果您有 bash 4.3,您应该接受 @chepner 的回答 - 这是最好的。

    【讨论】:

    • 您的代码可以解决问题。但发生了什么事?在 main 中,数组填充了新值。在函数中,var 似乎只带有主变量名(它不是一个数组,就像我想的那样)。
    • @il_mix 添加了解释
    • 强制性警告:eval 将执行您作为参数传递的 anything;它并不关心生成的字符串是否不仅仅是一个简单的数组赋值
    • @chepner - 是的,当然 - 在这种情况下,在 eval 之前,所有传递的参数都存储在 单引号 字符串中,这有点帮助。 .. ;) (应该严格清理数组名称)...
    • 请将eval "$var+=($(printf "'%s' " "$@"))";替换为eval "$var+=($(printf '%q ' "$@"))";
    【解决方案3】:

    您正在将元素添加到函数内部的数组变量中,但数组或任何其他变量未在 BASH 中作为引用传递。因此,函数内部对这些变量所做的任何更改都不会在函数外部可见。

    作为一种解决方法,您可以使用如下示例中的全局变量:

    # initialize an array
    myArray=("my item 0" "my item 1")
    
    useArrayInFunc { myArray+=("my item 2")); }
    
    useArrayInFunc
    
    printf "%s\n" "${myArray[@]}"
    my item 0
    my item 1
    my item 2
    

    【讨论】:

    • 我不喜欢使用全局变量
    • 由于我需要在不同的数组上使用该函数,然后对这些数组进行一些操作,所以我需要用不同的参数调用它。否则,我可以让函数对全局变量进行操作,并在函数调用后将全局数组值复制到实际数组中;这有点乏味...
    【解决方案4】:

    您需要将本地变量分配给全局变量:

    function fillArray {
      local arrayName=$1
      local ref=$arrayName[@]
      local array=("${!ref}")
      local i item key
    
      for i in 0 1 2 3
      do
        array+=("new item $i")
      done
    
      echo "Tot items in function: ${#array[@]}"
      for item in "${array[@]}"
      do
        echo $item
      done
      echo
    
      for key in "${!array[@]}"; do
        declare -g ${arrayName}["$key"]="${array[$key]}"
      done
    }
    

    【讨论】:

    • 我试过你的例子,但我得到这个错误:“声明:-g:无效选项”。我正在使用 bash 4.1.2
    • 无赖。在 bash 中处理数组就是这样一个 PITA。你可能想改变你的方法:调用函数来生成新项目以添加到数组中,但返回(通过'echo')一个换行符/空字节分隔的字符串。将元素添加到当前范围内的数组中,而不是函数中。
    • 确认-g在4.2版本中被添加到declare
    • bash 4.2 现在已经 3 岁了。有什么不能升级的原因吗?
    • 我现在在 Windows 计算机上,使用在线(旧)bash 服务。我将使用更新的 bash 在家进行测试。
    猜你喜欢
    • 2011-04-04
    • 2011-08-06
    • 2010-11-06
    • 2021-03-19
    • 1970-01-01
    • 2013-05-03
    • 2013-09-10
    • 2019-04-14
    相关资源
    最近更新 更多