web-dev-qa-db-fra.com

Bash mauvaise substitution avec sous-coque et sous-chaîne

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?

17
Matt

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.

10
Daniel Martin

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.

6
jilles

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.

6
Alanyst

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
1
bobpaul

$ {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'

0
user3304852

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
0
Larry