script programar programacion pasar parametros manejo ejemplos cadenas bash shell scripting

bash - programacion - programar shell script linux



Bash-Pasar argumentos por referencia (8)

Quiero preguntar si es posible pasar argumentos a una función de script por referencia:

es decir, para hacer algo que se vería así en C:

Void boo (int & myint) { myint= 5; } main (){ int t= 4; printf t; // t->4 boo (t); printf t; // t ->5 }

Entonces en BASH quiero hacer algo como:

Function boo () { var1=$1 # now var1 is global to the scrip but using it outside # this function makes me loose 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

Cualquier pensamiento sería apreciado.


Bash no tiene nada como referencias integradas, por lo que básicamente la única forma en que podría hacer lo que desea es pasarle a la función el nombre de la variable global que desea que modifique. E incluso entonces necesitarás una declaración de eval :

boo() { eval ${1}="new" } SOME_VAR="old" echo $SOME_VAR # old boo "SOME_VAR" echo $SOME_VAR # new

No creo que pueda usar referencias indirectas porque Bash accede automáticamente al valor de la variable cuyo nombre está almacenado en la referencia indirecta. No te da la oportunidad de configurarlo.


Eval nunca debe usarse en una cadena que un usuario puede establecer porque es peligrosa. Algo como "string; rm -rf ~" será malo. Por lo general, lo mejor es encontrar soluciones donde no tenga que preocuparse.

Sin embargo, se necesitará evaluar para establecer las variables aprobadas, como se señaló en el comentario.

$ y=four $ four=4 $ echo ${!y} 4 $ foo() { x=$1; echo ${!x}; } $ foo four 4


Use una función auxiliar 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 }

Y Newfun() dentro de Newfun() :

local "$1" && upvar $1 new

Para devolver múltiples variables, use otra función auxiliar upvars . Esto permite pasar múltiples variables dentro de una llamada, evitando así posibles conflictos si una llamada upvar cambia una variable utilizada en otra llamada upvar posterior.

Ver: http://www.fvue.nl/wiki/Bash:_Passing_variables_by_reference para upvars funciones upvars y más información.

El problema con:

eval $1=new

es que no es seguro si $1 contiene un comando:

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

Sería mejor usar printf -v :

printf -v "$1" %s new

Pero printf -v no puede asignar matrices.

Además, tanto eval como printf no funcionarán si la variable pasa a ser declarada local :

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

El conflicto permanece allí incluso si local b está unset :

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


Desde la página de manual de Bash (Expansión de parámetros):

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.

Por lo tanto, una referencia es el nombre de la variable. Aquí hay una función de swap que utiliza la indirección variable que no requiere una variable temporal:

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


He encontrado una manera de hacerlo, pero no estoy seguro de qué tan correcto es esto:

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

Entonces pasamos el nombre de la variable en lugar del valor y luego usamos eval ...


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

Salida:

cat cat|dog cat|dog|hamster

La estructura para llamar a esa función es:

append_string name_of_var_to_update string_to_add name_of_var_containing_sep_char

El nombre de la variable se pasa a fuction sobre PETS y SEP, mientras que la cadena para agregar se pasa de la manera habitual como valor. "$ {! 1}" se refiere a los contenidos de la variable global PETS. Al principio, esa variable está vacía y contens se agrega cada vez que llamamos a la función. El carácter separador se puede seleccionar según sea necesario. Las líneas de partida "eval" actualizan la variable PETS.


Bien, esta pregunta ha estado esperando una solución "real" desde hace algún tiempo, y me complace decir que ahora podemos lograr esto sin usar ninguna evaluación.

La clave para recordar es declarar una referencia tanto en la persona que llama como la llamada, al menos en mi ejemplo:

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


Esto es lo que funciona para mí en 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