如何从 bash 脚本中的函数内部连接到字符串变量?

How to concatenate to string variable from inside function in bash script?

我希望能够调用一个函数,并保留该函数将对字符串执行的所有修改。

例如: 我试过这个脚本

#!/bin/bash

change_string() {
  var=
  var+=",Hello2"
  echo "Function: $var"
}

myString="Hello1"
change_string $myString

echo "Main: $myString"

这将打印出:

Function: Hello1,Hello2
Main: Hello1

有没有什么方法可以更改函数调用中的 $myString,但将这些更改保留在 main 中?我想我可能错过了 bash 中与传递引用相关的内容?

Bash 是少数具有动态范围的语言之一(与通常的词法范围不同)。这意味着函数正在访问它们定义的最后一个范围内的变量(可能是赋值或带有 declarelocal 的声明)。

例如:

#!/bin/bash
fun() {
    x="newval"
}

x=start
echo "$x"
fun
echo "$x"

将打印:

start
newval

这个简单的例子看起来 x 是一个简单的全局变量,但它不止于此(作用域是动态嵌套的,总是访问 last/previous 作用域)。

因此,如果您想更改函数中的变量,只需更改即可。

#!/bin/bash
change_string() {
    myString+=",Hello2"
}

myString="Hello1"
change_string $myString

echo "Main: $myString"

你甚至可以更改变量"by reference",如果你传递变量的名称,然后使用declare -g设置(在外部范围内)通过将文本附加到当前值(通过间接扩展访问),我们知道其名称 (varname) 的变量的值:

change_string() {
    varname=""
    declare -g "$varname"="${!varname},Hello2"
}

myString="Hello1"
change_string myString

# myString is now "Hello1,Hello2"

使用 Bash 4.3+(namerefs!)

bash 的现代版本提供 "nameref" 支持,允许一个变量引用另一个。

#!/usr/bin/env bash

case $BASH_VERSION in
  [123].*|4.[012].*) echo "This needs bash 4.3 or newer" >&2; exit 1;;
esac

# local variable is prefixed because this will fail if we're passed a variable name we use
# internally; prefixing the local names makes such collisions unlikely.
change_string() {

  # make change_string__var an alias for the variable named in our argument
  declare -n change_string__var=

  # append to that variable
  change_string__var+=",Hello2"

  # ...and log the new value, 'cuz that's what the original code did.
  echo "Function: $change_string__var"
}

myString="Hello1"
change_string myString  ## pass variable **name**, not variable value
echo "Main: $myString"

With Bash 3.x+(间接求值+间接赋值)

或者,作为一种不太新颖的方法,可以使用 ${!var} 进行间接引用,并使用 printf -v 进行间接赋值。

#!/usr/bin/env bash

change_string() {

  # Store the variable name in a regular local variable
  local change_string__var=

  # Use ${!var} to get the value of the variable thus named
  local change_string__val=${!change_string__var}

  # Use ''printf -v varname ...'' to assign a new value
  printf -v "$change_string__var" %s "${change_string__val},Hello2"
}

myString="Hello1"
change_string myString
echo "Main: $myString"

您必须将变量的 名称 传递给您的函数,而不是它的值。一旦你这样做了,有几个选项。

最简单的可能是使用 nameref(需要 bash 4.3 或更高版本):

change_string() {
  declare -n var=
  var+=",Hello2"
}

$ foo="Hello1"
$ change_string foo
$ echo "$foo"
Hello1,Hello2

在引入 namerefs 之前,您可以使用 declare -g,这需要 bash 4.2 或更高版本:

change_string () {
  var=
  old_val=${!var}
  declare -g "=${old_val},Hello2"
}

在 4.2 之前,您几乎一直在使用 eval 并且祈祷您没有被代码注入攻击咬伤。

change_string () {
  var=
  eval "=$,Hello2"
}
不过,

None 中的方法完全是万无一失的;请参阅 Bash FAQ 048 的 this section。我的建议是尽量避免需要这样的功能。