¿Se pueden anidar las expresiones de expansión de parámetros de $ {var} en bash?

Lo que tengo es esto:

progname=${0%.*} progname=${progname##*/} 

¿Se puede anidar (o no) en una línea, es decir, una sola expresión?

Estoy tratando de quitarle el camino y la extensión al nombre de un script para que solo quede el nombre base. Las dos líneas anteriores funcionan bien. Mi naturaleza ‘C’ simplemente me está conduciendo a ofuscar estos aún más.

Si por nido, te refieres a algo como esto:

 #!/bin/bash export HELLO="HELLO" export HELLOWORLD="Hello, world!" echo ${${HELLO}WORLD} 

Entonces no, no puedes anidar expresiones ${var} . El expansor de syntax bash no lo entenderá.

Sin embargo, si entiendo bien su problema, podría usar el comando basename : quita la ruta de un nombre de archivo dado y, si se le da la extensión, también lo eliminará. Por ejemplo, ejecutar basename /some/path/to/script.sh .sh devolverá el script .

Bash apoya la expansión indirecta:

 $ FOO_BAR="foobar" $ foo=FOO $ foobar=${foo}_BAR $ echo ${foobar} FOO_BAR $ echo ${!foobar} foobar 

Esto debería apoyar la anidación que está buscando.

Un hilo antiguo pero quizás la respuesta es el uso de Indirection: $ {! PARAMETER}

Por ejemplo, considere las siguientes líneas:

 H="abc" PARAM="H" echo ${!PARAM} #gives abc 

La siguiente opción me ha funcionado:

  NAME="par1-par2-par3" echo $(TMP=${NAME%-*};echo ${TMP##*-}) 

La salida es:

  par2 

Esta anidación no parece posible en bash, pero funciona en zsh:

 progname=${${0%.*}##*/} 

En realidad, es posible crear variables anidadas en bash, usando dos pasos.

Aquí hay un script de prueba basado en la publicación de Tim, usando la idea sugerida por user1956358.

 #!/bin/bash export HELLO="HELLO" export HELLOWORLD="Hello, world!" # This command does not work properly in bash echo ${${HELLO}WORLD} # However, a two-step process does work export TEMP=${HELLO}WORLD echo ${!TEMP} 

El resultado es:

 Hello, world! 

Hay muchos trucos sencillos explicados ejecutando ‘ info bash ‘ desde la línea de comando, y luego buscando ‘ Expansión de parámetros de shell ‘. He estado leyendo algunos hoy mismo, perdí unos 20 minutos de mi día, pero mis guiones van a mejorar mucho …

Actualización: después de leer más, sugiero esta alternativa según su pregunta inicial.

 progname=${0##*/} 

Vuelve

 bash 

Las expresiones como ${${a}} no funcionan. Para solucionarlo, puede usar eval :

 b=value a=b eval aval=\$$a echo $aval 

La salida es

value

El basename bultin podría ayudar con esto, ya que estás dividiéndote específicamente en / en una parte:

 user@host# var=/path/to/file.extension user@host# basename ${var%%.*} file user@host# 

No es realmente más rápido que la variante de dos líneas, pero es solo una línea que usa la funcionalidad incorporada. O bien, use zsh / ksh, que puede hacer la anidación del patrón. 🙂

Sé que este es un hilo antiguo, pero aquí están mis 2 centavos.

Aquí hay una función bash (por cierto, kludgy) que permite la funcionalidad requerida:

 read_var() { set | grep ^$1\\b | sed s/^$1=// } 

Aquí hay un breve script de prueba:

 #!/bin/bash read_var() { set | grep ^$1\\b | sed s/^$1=// } FOO=12 BAR=34 ABC_VAR=FOO DEF_VAR=BAR for a in ABC DEF; do echo $a = $(read_var $(read_var ${a}_VAR)) done 

La salida es, como se esperaba:

 ABC = 12 DEF = 34 

Hay una solución de 1 línea para la pregunta original de OP, el nombre básico de un script con la extensión de archivo eliminada:

 progname=$(tmp=${0%.*} ; echo ${tmp##*/}) 

Aquí hay otro, pero, usando un truco para el nombre base:

 progname=$(basename ${0%.*}) 

Otras respuestas se han apartado de la pregunta original de OP y se centraron en si es posible simplemente expandir el resultado de expresiones con ${!var} pero se encontraron con la limitación de que var debe coincidir explícitamente con un nombre de variable. Una vez dicho esto, no hay nada que le impida tener una respuesta de 1 línea si encadena las expresiones junto con un punto y coma.

 ANIMAL=CAT BABYCAT=KITTEN tmp=BABY${ANIMAL} ; ANSWER=${!tmp} # ANSWER=KITTEN 

Si desea hacer que esto parezca una sola statement, puede anidarla en una subcadena, es decir,

 ANSWER=$( tmp=BABY${ANIMAL) ; echo ${!tmp} ) # ANSWER=KITTEN 

Un uso interesante es la indirección que funciona en los argumentos de una función bash. Luego, puede anidar sus llamadas a la función bash para lograr indirección anidada multinivel porque podemos hacer comandos nesteds:

Aquí hay una demostración de indirección de una expresión:

 deref() { echo ${!1} ; } ANIMAL=CAT BABYCAT=KITTEN deref BABY${ANIMAL} # Outputs: KITTEN 

Aquí hay una demostración de direccionamiento a nivel múltiple a través de comandos nesteds:

 deref() { echo ${!1} ; } export AA=BB export BB=CC export CC=Hiya deref AA # Outputs: BB deref $(deref AA) # Outputs: CC deref $(deref $(deref AA)) # Outputs: Hiya 

Si la motivación es “ofuscar” (yo diría racionalizar) el procesamiento de matrices en el espíritu de las “comprensiones” de Python, cree una función auxiliar que realice las operaciones en secuencia.

 function fixupnames() { pre=$1 ; suf=$2 ; shift ; shift ; args=($@) args=(${args[@]/#/${pre}-}) args=(${args[@]/%/-${suf}}) echo ${args[@]} } 

Puedes usar el resultado con un buen trazador de líneas.

 $ echo $(fixupnames ab abc def ghi) a-abc-b a-def-b a-ghi-b 

Aunque este es un hilo muy antiguo, este dispositivo es ideal para seleccionar directa o aleatoriamente un archivo / directorio para procesarlo (reproducir melodías, escoger una película para mirar o reservar para leer, etc.).

En bash, creo que generalmente es cierto que no se pueden anidar directamente dos expansiones del mismo tipo, pero si se pueden separar con algún tipo de expansión diferente, se puede hacer.

 e=($(find . -maxdepth 1 -type d)) c=${2:-${e[$((RANDOM%${#e[@]}))]}} 

Explicación: e es una matriz de nombres de directorio, c el directorio seleccionado, con un nombre explícito de $ 2,

 ${2:-...} 

donde … es la selección aleatoria alternativa dada por

 ${e[$((RANDOM%${#e[@]}))]} 

donde el

 $((RANDOM%...)) 

número generado por bash se divide por el número de elementos en el conjunto e, dado por

 ${#e[@]} 

produciendo el rest (del operador%) que se convierte en el índice de la matriz e

 ${e[...]} 

Por lo tanto, tiene cuatro expansiones anidadas.

eval te permitirá hacer lo que estás buscando:

 export HELLO="HELLO" export HELLOWORLD="Hello, world!" eval echo "\${${HELLO}WORLD}" 

Salida: Hello, world