【问题标题】:Bash function with an array input and output具有数组输入和输出的 Bash 函数
【发布时间】:2017-07-11 21:24:28
【问题描述】:

如果我在 bash shell 中定义一个数组:

a=()
a+=("A")
a+=("B")
a+=("C")

我可以按预期与之交互:

echo "${a[0]}"
# Returns "A"

echo "${a[1]}"
# Returns "B"

但是当我通过一个函数运行同一个数组时,我一定是做错了什么。首先,我将定义我的函数:

function sort_array {
  declare -a array=("${!1}")
  local sorted=()

  sorted+=("1")
  sorted+=("2")
  sorted+=("3")

  echo "${sorted[@]}"
}

现在让我们调用它并检查结果:

b=()
b=$(sort_array a[@])
echo "${b[0]}"

# Returns "1 2 3"
# But I'm expecting b[0] == 1

我做错了什么?我意识到我的示例可以完全删除函数参数,但我的最终目标是编写一个 bash sort_array() 函数,我可以将一个数组传递给该函数并取回一个数组。

【问题讨论】:

  • 返回的是函数内部的回显,而不是b的结果...返回空。
  • 在函数调用之后添加b=($b) 会得到预期的结果。对数组变量的字符串赋值只会将该值放在第一个索引中。
  • @hbagdi 做到了!我最终将分配更改为b=($(sort_array a[@]))。如果您想将其发布为答案,我会接受。
  • 这通常不起作用;它将一个包含空格的元素拆分为多个元素,这通常违背了使用数组的目的。
  • 我已经多次阅读了这个问题和下面的答案,但是有一些事情让我很困扰......在所有提出的解决方案中,最终排序的数组与传入的原始数组完全无关? ?此外,也没有任何类型的排序。似乎每个人都在研究如何仅在 bash 中传递数组。 chepner 确实至少与原始数组有关,但所有其他解决方案,同时将初始数组设置为传入的数组,然后创建另一个完全没有关系的数组并返回它

标签: arrays bash function shell


【解决方案1】:

正如@chepner 所说,bash 没有数组值。当您将数组传递给函数时,您真正要做的是将数组的每个元素作为单独的参数传递给该函数。

所有的 shell 函数都可以return 是一个单字节的退出代码值,0-255。他们可以返回任何其他内容的唯一方法是输出它,使用echoprintf 或其他任何东西;然后,调用者必须以任何常用方式(命令替换、进程替换、重定向到要读取的文件等)来捕获该输出。

也就是说,如果您在调用中添加一些语法,您的原始代码就可以工作:

b=($(sort_array "${a[@]}"))

但这依赖于排序数组的元素是被解析为单个单词的字符串。更安全的版本是将sort_array 函数更改为每行打印一个元素;然后调用者可以使用内置的mapfile 将这些行读入一个数组(别名readarray;需要Bash 4.x)。看起来像这样:

function sort_array {
  declare -a array=("$@")
  local sorted=()

  sorted+=("1")
  sorted+=("2")
  sorted+=("3")

  printf '%s\n' "${sorted[@]}"
}
mapfile -t b < <(sort_array "${a[@]}")

这表示从&lt;(...) 内部命令的输出中读取数组b-t 告诉它不要在数组值中包含换行符。

更安全的是使用空字符而不是换行符;如果你有 bash 4.4 最简单的方法,它为 mapfile 添加了一个选项来使用不同的字符代替换行符:

function sort_array {
  declare -a array=("$@")
  local sorted=()

  sorted+=("1")
  sorted+=("2")
  sorted+=("3")

  printf '%s\0' "${sorted[@]}"
}
mapfile -t -d '\0' b < <(sort_array "${a[@]}")

【讨论】:

  • 请注意,mapfilebash 4.4 之前没有获得-d 选项。
  • 另外,您仍然将$1 视为a[@],但在示例调用中使用"${a[0]}"
  • 谢谢,@chepner。固定。
  • 感谢这个非常有用的解释!这既直接回答了我的问题,又提供了一个整体上更好的解决方案。
【解决方案2】:

bash 没有数组。语句echo "${sorted[@]}" 不会“返回”数组值,它只是将数组的每个元素写入标准输出,用一个空格分隔。 (更具体地说,数组扩展产生一个单词序列,每个元素一个,然后作为参数传递给echo。)

bash 中模拟有点困难。您必须创建一个全局数组参数,这是在 bash 4.2 之前无法在函数内部执行的操作。在 bash 4.3 中引入 namerefs 之前,使用上述数组很困难。

sort_array () {
    declare -n input=$1     # Local reference to input array
    declare -ga "$2"        # Create the output array
    declare -n output="$2"  # Local reference to output array

    # As a simple example, just reverse the array instead
    # of sorting it.
    n=${#input[@]}
    for((i=n-1; i>=0; i--)); do
        echo "*** ${input[i]}"
        output+=( "${input[i]}" )
    done
}

现在,您传递 sort_array 两个参数,分别是输入和输出数组的名称。

$ a=("foo 1" "bar 2" "baz 3")
$ sort_array a b
$ echo "${b[0]}"
baz 3

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-06-21
    • 1970-01-01
    • 1970-01-01
    • 2019-11-26
    相关资源
    最近更新 更多