我想问问,如果能够通过参一脚本的功能是通过参考:

即做的东西,这看起来像这样用C++:

void boo(int &myint) { myint = 5; }

int main() {
    int t = 4;
    printf("%d\n", t); // t->4
    boo(t);
    printf("%d\n", t); // t->5
}

所以然后在砸我想要做的事,如:

function boo () 
{

    var1=$1       # now var1 is global to the script but using it outside
                  # this function makes me lose encapsulation

    local var2=$1 # so i should use a local variable ... but how to pass it back?

    var2='new'    # only changes the local copy 
    #$1='new'     this is wrong of course ...
    # ${!1}='new' # can i somehow use indirect reference?
}           

# call boo
SOME_VAR='old'
echo $SOME_VAR # -> old
boo "$SOME_VAR"
echo $SOME_VAR # -> new

任何想法,将不胜感激。

有帮助吗?

解决方案

这是2018年,这个问题应该得到更新。至少在庆典,庆典4.3-阿尔法,你可以使用 namerefs 通过功能的论据通过参考:

function boo() 
{
    local -n ref=$1
    ref='new' 
}

SOME_VAR='old'
echo $SOME_VAR # -> old
boo SOME_VAR
echo $SOME_VAR # -> new

关键的作品这里是:

  • 通过变量名称,嘘,没有其价值: boo SOME_VAR, ,不 boo $SOME_VAR.

  • 内部的功能,使用 local -n ref=$1 宣布 nameref 可变的名字 $1, ,这意味着它不是一个参照 $1 本身,而是以一个可变的 他的名字 $1 持有,即 SOME_VAR 在我们的情况。值在右手边应该只是一串的命名一个现有的变量:不论你如何获得串,因此喜欢的东西 local -n ref="my_var"local -n ref=$(get_var_name) 会工作。 declare 还可以代替 local 在环境允许的/需要。看看 一章壳的参数在庆典的参考手册 更多的信息。

这种方法的优点是(可以说是)更好的可读性,而最重要的是,避免了 eval, ,他们的安全隐患是许多记录在案。

其他提示

从击手册页(参数扩展):

    If the first  character of parameter is an exclamation  point (!), a
    level of variable indirection is  introduced. Bash uses the value of
    the variable  formed from the rest  of parameter as the  name of the
    variable; this variable  is then expanded and that value  is used in
    the rest  of the  substitution, rather than  the value  of parameter
    itself. This is known as indirect expansion.

因此基准是变量名。下面是一个使用swap功能 变量间接不需要临时变量:

function swap()
{   # 
    # @param VARNAME1 VARNAME2
    #
    eval "$1=${!2} $2=${!1}"
}

$ a=1 b=2
$ swap a b
$ echo $a $b
2 1

使用一个辅助函数upvar

# Assign variable one scope above the caller.
# Usage: local "$1" && upvar $1 value [value ...]
# Param: $1  Variable name to assign value to
# Param: $*  Value(s) to assign.  If multiple values, an array is
#            assigned, otherwise a single value is assigned.
# NOTE: For assigning multiple variables, use 'upvars'.  Do NOT
#       use multiple 'upvar' calls, since one 'upvar' call might
#       reassign a variable to be used by another 'upvar' call.
# See: http://fvue.nl/wiki/Bash:_Passing_variables_by_reference
upvar() {
    if unset -v "$1"; then           # Unset & validate varname
        if (( $# == 2 )); then
            eval $1=\"\$2\"          # Return single value
        else
            eval $1=\(\"\${@:2}\"\)  # Return array
         fi
    fi
}

和使用它像这样从Newfun()内:

local "$1" && upvar $1 new

有关返回多个变量,使用另一个辅助函数upvars。这允许使一个呼叫内的多个变量,从而避免了可能出现的冲突,如果一个呼叫upvar改变另一后续upvar呼叫使用的变量。

请参阅: http://www.fvue.nl/wiki/Bash:_Passing_variables_by_reference对于辅助函数upvars和更多的信息。

使用的问题:

eval $1=new

是,它如果$1恰好包含一个命令不是安全的:

set -- 'ls /;true'
eval $1=new  # Oops

这将是最好使用printf -v

printf -v "$1" %s new

但是printf -v不能分配阵列。

而且,无论evalprintf不会如果变量恰好被声明local工作:

g() { local b; eval $1=bar; }  # WRONG
g b                            # Conflicts with `local b'
echo $b                        # b is empty unexpected

在冲突留在那里即使local bunset

g() { local b; unset b; eval $1=bar; }  # WRONG
g b                                     # Still conflicts with `local b'
echo $b                                 # b is empty unexpected

我已经找到一种方法来做到这一点,但我不知道如何正确是这样的:

Newfun()
{
    local var1="$1"
    eval $var1=2
    # or can do eval $1=2 if no local var
}

var=1
echo  var is $var    # $var = 1
newfun 'var'         # pass the name of the variable…
echo now var is $var # $var = 2

因此,我们通过相对于该值,然后在变量名使用eval ...

击没有像内置到它引用任何东西,所以基本上你就可以做你想做什么的唯一方式是通过函数你想让它修改全局变量的名称。即使如此,您需要一种eval语句:

boo() {
    eval ${1}="new"
}

SOME_VAR="old"
echo $SOME_VAR # old
boo "SOME_VAR"
echo $SOME_VAR # new

我不认为你可以在这里使用间接引用,因为猛砸自动访问他的名字被存储在间接引用变量的值。它不会给你设置它的机会。

好了,这个问题一直在等待一段时间了“真实”的解决方案,现在,我很高兴地说,我们现在可以在不使用eval都做到这一点。

要记住的在两个主叫方为被叫方声明一个参考,至少在我的示例:

#!/bin/bash

# NOTE this does require a bash version >= 4.3

set -o errexit -o nounset -o posix -o pipefail

passedByRef() {

    local -n theRef

    if [ 0 -lt $# ]; then

        theRef=$1

        echo -e "${FUNCNAME}:\n\tthe value of my reference is:\n\t\t${theRef}"

        # now that we have a reference, we can assign things to it

        theRef="some other value"

        echo -e "${FUNCNAME}:\n\tvalue of my reference set to:\n\t\t${theRef}"

    else

        echo "Error: missing argument"

        exit 1
    fi
}

referenceTester() {

    local theVariable="I am a variable"

    # note the absence of quoting and escaping etc.

    local -n theReference=theVariable

    echo -e "${FUNCNAME}:\n\tthe value of my reference is:\n\t\t${theReference}"

    passedByRef theReference

    echo -e "${FUNCNAME}:\n\tthe value of my reference is now:\n\t\t${theReference},\n\tand the pointed to variable:\n\t\t${theVariable}"

}

# run it

referenceTester

评估不应该对用户可以设置由于其危险的字符串一起使用。喜欢的东西“的字符串; RM -rf〜”会坏。所以一般最好找到解决办法,你不必担心。

然而,将需要的eval来设置传递的变量,因为评论指出。

$ y=four
$ four=4
$ echo ${!y}
4
$ foo() { x=$1; echo ${!x}; }
$ foo four
4
#!/bin/bash

append_string()
{
if [ -z "${!1}" ]; then
eval "${1}='$2'"
else
eval "${1}='${!1}''${!3}''$2'"
fi
}

PETS=''
SEP='|'
append_string "PETS" "cat" "SEP"
echo "$PETS"
append_string "PETS" "dog" "SEP"
echo "$PETS"
append_string "PETS" "hamster" "SEP"
echo "$PETS"

输出:

cat
cat|dog
cat|dog|hamster

用于调用该函数的结构是:

append_string  name_of_var_to_update  string_to_add  name_of_var_containing_sep_char

变量的名称被传递而字符串追加传递通常的方式为值机能的研究约PETS和九月“$ {!1}” 是指全球PETS变量的内容。在开始这个变量是空的,含量研究每天我们调用函数时添加。可以根据需要来选择分隔符。 “EVAL” 起始行更新PETS变量。

这对我来说是什么工作在Ubuntu的bash shell

#!/bin/sh

iteration=10

increment_count()
{
  local i
  i=$(($1+1))
  eval ${1}=\$i
}


increment_count iteration
echo $iteration #prints 11
increment_count iteration
echo $iteration #prints 12
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top