Un exemple artificiel ... étant donné
FOO="/foo/bar/baz"
cela fonctionne (en bash)
BAR=$(basename $FOO) # result is BAR="baz"
BAZ=${BAR:0:1} # result is BAZ="b"
cela ne veut pas
BAZ=${$(basename $FOO):0:1} # result is bad substitution
Ma question est la suivante: quelle règle entraîne l'évaluation incorrecte de cette [substitution de sous-shell]? Et quelle est la bonne façon, le cas échéant, de le faire en 1 saut?
Tout d'abord, notez que lorsque vous dites ceci:
BAR=$(basename $FOO) # result is BAR="baz"
BAZ=${BAR:0:1} # result is BAZ="b"
le premier bit de la construction pour BAZ
est BAR
et non la valeur dont vous voulez prendre le premier caractère. Ainsi, même si bash permettait aux noms de variables de contenir des caractères arbitraires, le résultat de la seconde expression ne serait pas ce que vous souhaitiez.
Cependant, en ce qui concerne la règle qui empêche cela, permettez-moi de citer la page de manuel 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.
Puis un peu plus tard:
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).
Et plus tard, quand il définit la syntaxe dont vous parlez:
${parameter:offset:length}
Substring Expansion. Expands to up to length characters of
parameter starting at the character specified by offset.
Ainsi, les règles énoncées dans la page de manuel indiquent que la construction ${foo:x:y}
doit avoir un paramètre en tant que première partie et qu'un paramètre ne peut être qu'un nom, un nombre ou l'un des rares caractères de paramètre spéciaux. $(basename $FOO)
n'est pas l'une des possibilités autorisées pour un paramètre.
Pour ce faire, utilisez un canal d’acheminement vers d’autres commandes, comme indiqué dans d’autres réponses.
Les formes modifiées de substitution de paramètres telles que ${parameter#Word}
peuvent uniquement modifier un paramètre, pas un mot arbitraire.
Dans ce cas, vous pouvez diriger la sortie de basename
vers une commande dd, comme
BAR=$(basename -- "$FOO" | dd bs=1 count=1 2>/dev/null)
(Si vous voulez un nombre plus élevé, augmentez count
et non bs
, sinon vous obtiendrez moins d'octets que ceux demandés.)
Dans le cas général, il n’ya aucun moyen de faire de telles choses en une seule tâche.
Il échoue car ${BAR:0:1}
est une extension variable. Bash s'attend à voir un nom de variable après ${
, pas une valeur.
Je ne suis pas au courant d'un moyen de le faire en une seule expression.
Comme d'autres l'ont dit, le premier paramètre de $ {} doit être un nom de variable. Mais vous pouvez utiliser un autre sous-shell pour évaluer approximativement ce que vous essayez de faire.
Au lieu de:
BAZ=${$(basename $FOO):0:1} # result is bad substitution
Utilisation:
BAZ=$(_TMP=$(basename $FOO);${_TMP:0:1}) # this works
$ {chaîne: 0: 1}, la chaîne doit être un nom de variable
par exemple:
FOO = "/ toto/bar/baz"
baz = "foo"
BAZ = eval echo '${'"$(basename $FOO)"':0:1}'
echo $ BAZ
le résultat est 'f'
Une solution artificielle pour votre exemple inventé:
BAZ=$(expr $(basename $FOO) : '\(.\)')
un péché
$ FOO=/abc/def/ghi/jkl
$ BAZ=$(expr $(basename $FOO) : '\(.\)')
$ echo $BAZ
j