Tengo una función bash para establecer $PATHasí:
assign-path()
{
str=$1
# if the $PATH is empty, assign it directly.
if [ -z $PATH ]; then
PATH=$str;
# if the $PATH does not contain the substring, append it with ':'.
elif [[ $PATH != *$str* ]]; then
PATH=$PATH:$str;
fi
}
Pero el problema es que tengo que escribir diferentes funciones para diferentes variables (por ejemplo, otra función para me $CLASSPATHgusta, assign-classpath()etc.). No pude encontrar una manera de pasar argumentos a la función bash para poder acceder a ella por referencia.
Sería mejor si tuviera algo como ...
assign( bigstr, substr )
{
if [ -z bigstr ]; then
bigstr=substr;
elif [[ bigstr != *str* ]]; then
bigstr=bigstr:substr;
fi
}
Alguna idea, ¿cómo lograr algo como lo anterior en bash?
bash
bash-script
ramgorur
fuente
fuente

assign-path /abcNo anexará/abcal PATH $ PATH si ya contiene/abc/def,/abcd,/def/abcetc. Sobre todo no se puede agregar/binsi la ruta ya contiene/usr/bin.$PATHy prueba negate contra sus argumentos como:add=/bin dir=/usr/bin ; [ -z "${dir%"$add"}" ] || dir="${dir}:${add}". En mi respuesta, lo hago de esta manera con tantos argumentos como quieras solo usandoIFS=:.$PATH? y Agregar directorio a$PATHsi aún no está allí (en Super Usuario ).Respuestas:
En
bashpuede utilizar${!varname}para expandir la variable referenciada por el contenido de otra. P.ej:Desde la página del manual:
Además, para establecer una variable referenciada por los contenidos (sin los peligros de
eval), puede usardeclare. P.ej:Por lo tanto, podría escribir su función de esta manera (tenga cuidado porque si usa
declareuna función, debe dar-go la variable será local):Y úsalo como:
Tenga en cuenta que también he corregido un error en el que si
substrya es una subcadena de uno de los miembros separados por dos puntosbigstr, pero no su propio miembro, entonces no se agregaría. Por ejemplo, esto permitiría agregar/bina unaPATHvariable que ya contiene/usr/bin. Utiliza losextglobconjuntos para hacer coincidir el principio / final de la cadena o dos puntos y luego cualquier otra cosa. Sinextglob, la alternativa sería:fuente
-gindeclareno está disponible en la versión anterior de bash, ¿hay alguna manera de que pueda hacer que esto sea compatible con versiones anteriores?exportpara ponerlo en su entorno (a riesgo de sobrescribir algo importante) oeval(varios problemas, incluida la seguridad, si no tiene cuidado). Si lo usaeval, debería estar bien si lo hace asíeval "$target=\$substr". Sin\embargo, si olvida esto, potencialmente ejecutará un comando si hay un espacio en el contenido desubstr.Nuevo en bash 4.3, es la
-nopción dedeclare&local:Eso se imprime
hello, world.fuente
Puede usar
evalpara establecer un parámetro. Una descripción de este comando se puede encontrar aquí . El siguiente uso deevales incorrecto:incorrecto(){ eval $ 1 = $ 2 }Con respecto a la evaluación adicional
eval, debe usarasignar(){ eval $ 1 = '$ 2' }Verifique los resultados del uso de estas funciones:
Pero puede lograr su objetivo sin el uso de
eval. Prefiero esta manera que es más simple.La siguiente función hace la sustitución de la manera correcta (espero)
aumentar(){ CORRIENTE local = $ 1 AUMENTO local = $ 2 local NUEVO si [[-z $ CORRIENTE]]; luego NUEVO = $ AUGMENT elif [[! (($ CORRIENTE = $ AUGMENT) || ($ CORRIENTE = $ AUGMENT: *) || \ ($ CORRIENTE = *: $ AUMENTO) || ($ CORRIENTE = *: $ AUGMENT: *))]]; luego NUEVO = $ CORRIENTE: $ AUMENTO más NUEVO = $ CORRIENTE fi echo "$ NUEVO" }Comprueba la siguiente salida
Ahora puede usar la
augmentfunción de la siguiente manera para establecer una variable:fuente
v='echo "OHNO!" ; var' ; l=val ; eval $v='$l'- haría eco de "OHNO! Antes de asignar var. Puede" $ {v ## * [; "$ IFS"]} = '$ l' "para asegurarse de que la cadena no se puede expandir a nada que no será evaluado con el =.=declaración de asignación. Puede argumentar que mi script no verifica su argumento. Eso es verdad. Ni siquiera verifico si hay algún argumento o si el número de argumentos es válido. Pero esto fue por intención. El OP puede agregar tales controles si lo desea.v(mejor su valor) como segundo argumento de la función de asignación. Por lo tanto, su valor debe estar en el lado derecho de una asignación. Es necesario citar el argumento de la funciónassign. Agregué esta sutileza a mi publicación.Con algunos trucos, puede pasar parámetros con nombre a funciones, junto con arreglos (probados en bash 3 y 4).
El método que desarrollé le permite acceder a los parámetros pasados a una función como esta:
En otras palabras, no solo puede llamar a sus parámetros por sus nombres (lo que compensa un núcleo más legible), sino que también puede pasar matrices (y referencias a variables; ¡esta función solo funciona en bash 4.3)! Además, todas las variables asignadas están en el ámbito local, igual que $ 1 (y otras).
El código que hace que esto funcione es bastante ligero y funciona tanto en bash 3 como en bash 4 (estas son las únicas versiones con las que lo he probado). Si está interesado en más trucos como este que hacen que desarrollar con bash sea mucho más fácil y fácil, puede echar un vistazo a mi Bash Infinity Framework , el código a continuación fue desarrollado para ese propósito.
fuente
¿Esto encaja?
fuente
evales susceptible a la ejecución arbitraria de comandos.evallíneas zhe sean más seguras? Normalmente, cuando creo que necesito evaluar, decido no usar * sh y cambiar a un idioma diferente. Por otro lado, al usar esto en scripts para agregar entradas a algunas variables similares a PATH, se ejecutará con constantes de cadena y no entrada del usuario ...evalmanera segura, pero requiere mucha reflexión. Si solo está tratando de hacer referencia a un parámetro, querría hacer algo como esto: deeval "$1=\"\$2\""esa manera, en laeval'sprimera pasada, solo evalúa $ 1 y en la segunda, tiene valor = "$ 2". Pero debe hacer otra cosa, esto es innecesario aquí."${1##*[;"$IFS"]}=\"\$2\"", e incluso eso no tiene garantía. Oeval "$(set -- $1 ; shift $(($#-1)) ; echo $1)=\"\$2\"". No es fácil.Los argumentos con nombre simplemente no son cómo se diseñó la sintaxis de Bash. Bash fue diseñado para ser una mejora iterativa sobre el shell Bourne. Como tal, debe asegurarse de que ciertas cosas funcionen entre los dos depósitos tanto como sea posible. Por lo tanto, no está destinado a ser más fácil de escribir en general, solo está destinado a ser mejor que Bourne al tiempo que garantiza que si lleva un script de un entorno Bourne a
bashlo más fácil posible. Eso no es trivial ya que muchos proyectiles todavía tratan a Bourne como un estándar de facto. Dado que las personas escriben sus scripts para que sean compatibles con Bourne (para esta portabilidad), la necesidad sigue vigente y es poco probable que cambie.Probablemente sea mejor mirar un script de shell diferente (como
pythono algo) por completo si es factible. Si se encuentra con las limitaciones de un idioma, debe comenzar a usar un nuevo idioma.fuente
bashla vida esto era cierto. Pero ahora se hacen provisiones específicas. Las variables de referencia completas ahora están disponibles enbash 4.3- vea la respuesta de derobert .Con la
shsintaxis estándar (funcionaría enbash, y no solo enbash), podría hacer:Al igual que para las soluciones que usan
bash'sdeclare, es seguro siempre que$1contenga un nombre de variable válido.fuente
EN ARGOS NOMBRADOS:
Esto se hace de manera muy simple y
bashno se requiere en absoluto: este es el comportamiento básico de asignación especificado por POSIX a través de la expansión de parámetros:Para hacer una demostración de una manera similar a @Graeme, pero de forma portátil:
Y allí solo hago
str=para asegurarme de que tenga un valor nulo, porque la expansión de parámetros tiene la protección incorporada contra la reasignación del entorno de shell en línea si ya está configurado.SOLUCIÓN:
Para su problema específico, no creo que los argumentos nombrados sean necesarios, aunque ciertamente son posibles. Use en su
$IFSlugar:Esto es lo que obtengo cuando lo ejecuto:
¿Notó que solo agregó los argumentos que aún no estaban incluidos
$PATHo que aparecieron antes? ¿O incluso que tomó más de un argumento?$IFSes útilfuente
assignaquí. Si tiene preguntas sobre cómo funciona, me complacería responder. Y, por cierto, si realmente quieres argumentos con nombre, quizás quieras ver esta otra respuesta mía en la que muestro cómo declarar una función nombrada para los argumentos de otra función: unix.stackexchange.com/a/120531/52934No puedo encontrar nada como rubí, pitón, etc., pero esto se siente más cerca de mí.
La legibilidad es mejor en mi opinión, 4 líneas son excesivas para declarar los nombres de sus parámetros. También se parece más a los idiomas modernos.
fuente
a=$1 b=$2 ...funciona igual de bien