【问题标题】:How to concatenate to string variable from inside function in bash script?如何从bash脚本中的函数内部连接到字符串变量?
【发布时间】:2017-11-08 22:33:09
【问题描述】:

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

例如: 我试过这个脚本

#!/bin/bash

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

myString="Hello1"
change_string $myString

echo "Main: $myString"

这将打印出来:

Function: Hello1,Hello2
Main: Hello1

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

【问题讨论】:

  • 顺便说一句,这听起来有点像一个用例,您可能应该首先使用数组而不是字符串。
  • 我将它用于clang-offload-bundler,它使用带有命令分隔参数的选项。感谢您的帮助!

标签: string bash append string-concatenation


【解决方案1】:

使用 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=$1

  # 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"

使用 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=$1

  # 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"

【讨论】:

  • 酷,但现在我的变量不再信任函数了!
  • printf 的不正当使用 :)
  • 我认为namerefs 正是我所需要的。感谢您为我提供了 1 个以上的 bash 版本。
【解决方案2】:

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

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

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

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

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

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

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

change_string () {
  var=$1
  eval "$1=\$$1,Hello2"
}

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

【讨论】:

  • 和往常一样,我忘记了查尔斯·达菲指出的printf -v
【解决方案3】:

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

例如:

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

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

将打印:

start
newval

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

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

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

myString="Hello1"
change_string $myString

echo "Main: $myString"

您甚至可以“通过引用”更改变量,如果您传递 变量的名称,然后使用 declare -g 设置(在外部范围内)我们名称的变量的值通过将文本附加到当前值(通过间接扩展访问)来了解 (varname):

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

myString="Hello1"
change_string myString

# myString is now "Hello1,Hello2"

【讨论】:

  • 关于通过更改变量来更改变量的观点,这是如何工作的?由于大多数其他人都建议使用 namerefs
  • 如果你知道变量的名字,你可以直接改。如果要传递变量的名称,然后更改引用的变量,可以使用declare -g 或通过declare -n 的namerefs。
  • 好的,谢谢,有道理,我想这取决于使用情况。
猜你喜欢
  • 2018-12-07
  • 2015-12-18
  • 1970-01-01
  • 2011-05-10
  • 1970-01-01
  • 2019-01-08
  • 1970-01-01
相关资源
最近更新 更多