Вопрос:

Как объединить строковую переменную изнутри функции в скрипте bash?

string bash append string-concatenation

737 просмотра

3 ответа

113 Репутация автора

Я хочу иметь возможность вызывать функцию и сохранять все изменения, которые функция будет выполнять в строке.

Например: я попробовал этот скрипт

#!/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, связанное с передачей по ссылке?

Автор: Enye Aaron Shi Источник Размещён: 08.11.2017 10:33

Ответы (3)


0 плюса

13465 Репутация автора

Bashэто один из редких языков, который имеет динамическую область видимости (в отличие от обычной лексической области видимости). Это означает, что функции обращаются к переменным из последней области, в которой они были определены (это может быть присваивание или объявление с declareили local).

Например:

#!/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"
Автор: randomir Размещён: 08.11.2017 10:50

3 плюса

194249 Репутация автора

Решение

С 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"
Автор: Charles Duffy Размещён: 08.11.2017 10:52

1 плюс

290562 Репутация автора

Вы должны передать имя переменной вашей функции, а не ее значение. Как только вы это сделаете, есть несколько вариантов.

Самым простым, вероятно, является использование nameref (для которого требуется bash4.3 или более поздняя версия ):

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

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

До того, как namerefs были введены, вы могли использовать тот declare -g, который требовал bash4.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. Мой совет - избегать такой функции.

Автор: chepner Размещён: 08.11.2017 11:01
Вопросы из категории :
32x32