Frage

Ich möchte fragen, ob es möglich ist, Argumente für eine Skriptfunktion durch Verweis übergeben:

d. zu tun, etwas, das wie das in C ++ aussehen würde:

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
}

Also dann in BASH Ich möchte etwas tun:

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

würde Irgendwelche Gedanken geschätzt.

War es hilfreich?

Lösung

Es ist 2018, und diese Frage verdient ein Update. Zumindest in Bash, wie der Bash 4.3-alpha, können Sie namerefs Funktionsargumente durch Verweis übergeben:

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

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

Die kritischen Stücke befinden sich hier:

  • Passing den Namen der Variablen boo, nicht seinen Wert. boo SOME_VAR , nicht boo $SOME_VAR

  • Innerhalb der Funktion, mit local -n ref=$1 a nameref in die Variable durch $1 genannt zu erklären, es ist kein Referenz Sinn sich $1, sondern vielmehr ein Variable , dessen Name $1 gilt, dh SOME_VAR in unserem Fall. Der Wert auf der rechten Seite sollte nur eine Zeichenfolge sein, eine vorhandene Variable Benennung: es spielt keine Rolle, wie Sie die Zeichenfolge erhalten, so Dinge wie local -n ref="my_var" oder local -n ref=$(get_var_name) auch funktionieren würde. declare können auch local in Kontexten ersetzen, mit denen / verlangen, dass. Siehe Kapitel über Shell-Parameter in Bash Reference Manual weitere Informationen.

Der Vorteil dieses Ansatzes ist (wohl) eine bessere Lesbarkeit und, was am wichtigsten ist, zu vermeiden eval, deren Sicherheit Gefahren sind vielfältig und gut dokumentiert.

Andere Tipps

Von der Bash Mann-Seite (Parameter Expansion):

    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.

Daher ist eine Referenz ist der Name der Variablen. Hier ist eine swap Funktion Variable Umleitung, die nicht eine temporäre Variable erfordert:

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

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

Verwenden Sie eine Hilfsfunktion 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
}

Und verwenden Sie es wie folgt aus Newfun():

local "$1" && upvar $1 new

mehrere Variablen Für die Wieder, verwenden Sie eine andere Hilfsfunktion upvars. Auf diese Weise können mehrere Variablen innerhalb eines Anrufs vorbei, so vermeidet mögliche Konflikte, wenn man upvar Aufruf eine Variable in einem anderen nachfolgenden upvar Aufruf verwendet ändert.

Siehe auch: http://www.fvue.nl/wiki/Bash:_Passing_variables_by_reference für Hilfsfunktion upvars und weitere Informationen.

Das Problem mit:

eval $1=new

ist, dass es nicht sicher ist, ob $1 einen Befehl enthält passiert:

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

Es wäre besser, printf -v zu verwenden:

printf -v "$1" %s new

Aber printf -v können keine Arrays zuweisen.

Darüber hinaus sowohl eval und printf wird nicht funktionieren, wenn die Variable local zu erklären passiert:

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

Der Konflikt bleibt dort, selbst wenn local b ist unset:

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

Ich habe einen Weg gefunden, dies zu tun, aber ich bin nicht sicher, wie richtig das ist:

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

So wir die Variablennamen übergeben, wie auf den Wert gegenüber und dann verwenden eval ...

Bash hat nichts wie Referenzen hinein gebaut, so dass im Grunde der einzige Weg, Sie wären in der Lage zu tun, was Sie wollen, ist die Funktion der Namen der globalen Variablen zu übergeben Sie sich ändern mögen. Und selbst dann werden Sie eine eval Anweisung müssen:

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

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

Ich glaube nicht, Sie indirekten Verweisen hier verwenden können, weil Bash automatisch den Wert der Variablen, deren Namen zugreift, in der indirekten Referenz gespeichert. Dabei spielt es keine gibt Ihnen die Chance, es zu setzen.

Ok, so dass diese Frage für eine ‚echte‘ Lösung für einige Zeit wurde jetzt warten, und ich bin froh zu sagen, dass wir dies jetzt erreichen können alle eval ohne Verwendung.

Die Taste zu erinnern ist eine Referenz sowohl als Angerufener den Anrufer zu erklären, zumindest in meinem Beispiel:

#!/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

Eval sollte nie auf einer Zeichenfolge verwendet werden, die ein Benutzer wegen seiner gefährlichen einstellen. So etwas wie "string; rm -rf ~" wird schlecht sein. So im Allgemeinen ihre besten Lösungen zu finden, wo Sie müssen darüber keine Sorgen.

Allerdings wird eval benötigt, um die übergebenen Variablen zu setzen, wie der Kommentar zur Kenntnis genommen.

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

Ausgabe:

cat
cat|dog
cat|dog|hamster

Struktur für diese Funktion Aufruf lautet:

append_string  name_of_var_to_update  string_to_add  name_of_var_containing_sep_char

Name der Variablen ist bestanden, über Haustiere und SEP fuction während Zeichenfolge anhängen die übliche Art und Weise als Wert übergeben wird. „$ {! 1}“ bezieht sich auf Inhalte der globalen TIERE variabel. Am Anfang, dass Variable leer und contens wird jedes Mal, wenn wir rufen Sie die Funktion hinzugefügt. Trennzeichen können je nach Bedarf gewählt werden. "Eval" Startlinien TIER Variable aktualisieren.

Dies ist, was für mich auf Ubuntu Bash-Shell funktioniert

#!/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
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top