2011-05-06 9 views
16

Un ejemplo artificioso ... dadamal sustitución Bash con subnivel y subcadena

FOO="/foo/bar/baz" 

esto funciona (en bash)

BAR=$(basename $FOO) # result is BAR="baz" 
BAZ=${BAR:0:1}  # result is BAZ="b" 

esto no

BAZ=${$(basename $FOO):0:1} # result is bad substitution 

Mi pregunta ¿Cuál regla causa que [la sustitución de subshell] se evalúe incorrectamente? ¿Y cuál es la forma correcta, si la hay, para hacer esto en 1 salto?

Respuesta

10

En primer lugar, tenga en cuenta que cuando se dice esto:

BAR=$(basename $FOO) # result is BAR="baz" 
BAZ=${BAR:0:1}  # result is BAZ="b" 

el primer bit en la construcción de BAZ es BAR y no el valor que desea tomar el primer carácter de. Entonces, incluso si bash permitiera que los nombres de las variables contengan caracteres arbitrarios, su resultado en la segunda expresión no sería lo que usted desea.

Sin embargo, en cuanto a la regla de que está impidiendo esto, permítanme citar desde la página de manual de bash:

DEFINITIONS 
     The following definitions are used throughout the rest of this docu‐ 
     ment. 
     blank A space or tab. 
     word A sequence of characters considered as a single unit by the 
       shell. Also known as a token. 
     name A word consisting only of alphanumeric characters and under‐ 
       scores, and beginning with an alphabetic character or an under‐ 
       score. Also referred to as an identifier. 

A continuación, un poco más tarde:

PARAMETERS 
     A parameter is an entity that stores values. It can be a name, a num‐ 
     ber, or one of the special characters listed below under Special Param‐ 
     eters. A variable is a parameter denoted by a name. A variable has a 
     value and zero or more attributes. Attributes are assigned using the 
     declare builtin command (see declare below in SHELL BUILTIN COMMANDS). 

Y más tarde, cuando se define la sintaxis estás preguntando por:

${parameter:offset:length} 
      Substring Expansion. Expands to up to length characters of 
      parameter starting at the character specified by offset. 

lo que las reglas como se establece en la página de manual dice que el ${foo:x:y} constructo debe tener un parámetro como la primera parte, y que un parámetro solo puede ser un nombre, un número o uno de los pocos caracteres de parámetros especiales. $(basename $FOO) no es una de las posibilidades permitidas para un parámetro.

En cuanto a una manera de hacer esto en una tarea, use una tubería para otros comandos como se menciona en otras respuestas.

6

Las formas modificadas de sustitución de parámetros como ${parameter#word} solo pueden modificar un parámetro, no una palabra arbitraria.

En este caso, es posible canalizar la salida de basename a un comando dd, como

BAR=$(basename -- "$FOO" | dd bs=1 count=1 2>/dev/null) 

(Si desea un mayor recuento, aumentar count y no bs, de lo contrario puede obtener menos bytes que solicita .)

En el caso general, no hay forma de hacer cosas como esta en una tarea.

6

No funciona porque ${BAR:0:1} es una expansión variable. Bash espera ver un nombre de variable después de ${, no un valor.

No conozco una forma de hacerlo en una sola expresión.

0

$ {string: 0: 1}, de cadena debe ser un nombre de variable

por ejemplo:

FOO = "/ foo/bar/baz"

baz = "foo"

BAZ = eval echo '${'"$(basename $FOO)"':0:1}'

echo $ BAZ

el resultado i s 'f'

1

Como han dicho otros, el primer parámetro de $ {} debe ser un nombre de variable. Pero puede usar otra subshell para aproximarse a lo que está tratando de hacer.

En lugar de:

BAZ=${$(basename $FOO):0:1} # result is bad substitution 

Uso:

BAZ=$(_TMP=$(basename $FOO);${_TMP:0:1}) # this works 
0

Una solución ideada para su ejemplo artificioso:

BAZ=$(expr $(basename $FOO) : '\(.\)') 

como en

$ FOO=/abc/def/ghi/jkl 
$ BAZ=$(expr $(basename $FOO) : '\(.\)') 
$ echo $BAZ 
j 
Cuestiones relacionadas