Expansion d'accolade bash après une barre oblique

12

J'essaie de copier un fichier sous un nom différent dans le même répertoire en utilisant l'expansion d'accolade. J'utilise bash 4.4.18.

Voici ce que j'ai fait:

cp ~/some/dir/{my-file-to-rename.bin, new-name-of-file.bin}

mais je reçois cette erreur:

cp: cannot stat '/home/xyz/some/dir/{my-file-to-rename.bin,': No such file or directory

Même une simple expansion d'accolade comme celle-ci me donne la même erreur:

cp {my-file-to-rename.bin, new-name-of-file.bin}

Qu'est-ce que je fais mal?

nanangarsyade
la source

Réponses:

29

La syntaxe d' extension d'accolade accepte les virgules, mais elle n'accepte pas d'espace après la virgule. Dans de nombreux langages de programmation, les espaces après des virgules sont monnaie courante, mais pas ici. Dans Bash, la présence d'un espace sans guillemets empêche l'expansion de l'accolade.

Retirez l'espace, et cela fonctionnera:

cp ~/some/dir/{my-file-to-rename.bin,new-name-of-file.bin}

Bien que cela ne soit pas du tout requis, notez que vous pouvez déplacer la queue en .bindehors des accolades:

cp ~/some/dir/{my-file-to-rename,new-name-of-file}.bin

Si vous souhaitez tester l'effet de l'expansion de l'accolade, vous pouvez utiliser echoou printf '%s ', ou printfavec la chaîne de format que vous préférez, pour le faire. (Personnellement, je ne l'utilise que echopour cela, lorsque je suis dans Bash, car la fonction echointégrée de Bash ne développe pas les séquences d'échappement par défaut, et est donc raisonnablement bien adaptée pour vérifier quelle commande sera réellement exécutée.) Par exemple:

ek@Io:~$ echo cp ~/some/dir/{my-file-to-rename,new-name-of-file}.bin
cp /home/ek/some/dir/my-file-to-rename.bin /home/ek/some/dir/new-name-of-file.bin
Eliah Kagan
la source
23

string{foo, bar}n'est pas l'expansion d'accolade; ce sont les deux mots string{foo,et bar}. Pour utiliser l'expansion d'accolade, les accolades doivent être dans le même mot. Vous devrez soit supprimer l'espace supplémentaire si vous n'en avez pas besoin, soit le citer si vous en avez besoin:

$ printf "%s\n" aa{bb, cc}
aa{bb,
cc}
$ printf "%s\n" aa{bb,cc}
aabb
aacc
$ printf "%s\n" aa{bb," cc"}
aabb
aa cc
ilkkachu
la source
+1 pour l'exemple clair et merci. Je donnerai mon vote positif lorsque ma réputation sera suffisante. :)
nanangarsyad
Il y a autre chose qui devrait être mentionné à propos des "mots shell". . .comme comment Shell sait où diviser les mots;)
Sergiy Kolodyazhnyy
-1

Bash traite cet espace comme n'importe quel autre. Comme IFS, le séparateur de champ interne. Ceci est utilisé pour le fractionnement des mots après expansion et pour diviser les lignes en mots avec la commande intégrée read.

Le shell traite chaque caractère d'IFS comme un délimiteur et divise les résultats des autres extensions en mots sur ces caractères. Si IFS n'est pas défini, ou si sa valeur est exactement la valeur par défaut, les séquences de, et au début et à la fin des résultats des extensions précédentes sont ignorées, et toute séquence de caractères IFS qui n'est pas au début ou à la fin sert à délimiter mots. Si IFS a une valeur autre que la valeur par défaut, les séquences de l'espace et de la tabulation des caractères d'espacement sont ignorées au début et à la fin du mot, tant que le caractère d'espacement est dans la valeur d'IFS (un caractère d'espacement IFS). Tout caractère dans IFS qui n'est pas un espace blanc IFS, ainsi que tout caractère d'espace blanc IFS adjacent, délimite un champ. Une séquence de caractères blancs IFS est également traitée comme un délimiteur. Si la valeur de IFS est nulle,
-bash (1)

En insérant le délimiteur, sans échappement, vous avez dit à bash que votre commande et vos arguments sont:

  1. "cp"
  2. "~ / certains / dir / {mon-fichier-à-renommer.bin,"
  3. "nouveau-nom-de-fichier.bin}"

Si vous aviez eu des guillemets ou une échappatoire "\", vous auriez:

  1. "cp"
  2. "~ / certains / dir / {mon-fichier-à-renommer.bin, \ nouveau-nom-de-fichier.bin}"

Ce qui ne serait pas non plus ce que vous vouliez, sauf si "nouveau-nom-de-fichier.bin" est le nouveau nom de fichier que vous vouliez. Espace inclus. Comme l'expansion de l'accolade se produit en premier, puis l'expansion du tilde, bash s'exécuterait:

  1. "cp"
  2. "/path/to/home/some/dir/my-file-to-rename.bin"
  3. "/ chemin / vers / home / certains / dir / \ nouveau-nom-de-fichier.bin"

Le simple fait de supprimer l'espace réglerait tout cela.

cde
la source
5
C'est généralement bon, mais vous semblez dire que le fractionnement de mots se produit cp ~/some/dir/{my-file-to-rename.bin, new-name-of-file.bin}et IFSaffecte le résultat. Il n'en est pas de même. Ici, l'espace est un métacaractère de la tokenisation ( étape 2 ). Voir 3.5.7 sur le moment du fractionnement. Essayez IFS=xalors printf '[%s]\n' {a,b} printf '[%s]\n' {a, b} printf '[%s]\n' {a,xb} printf '[%s]\n' {a, xb}.
Eliah Kagan