Expansión de la llave bash después de una barra diagonal

12

Estoy tratando de copiar un archivo con un nombre diferente en el mismo directorio usando la expansión de llaves. Estoy usando bash 4.4.18.

Esto es lo que hice:

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

pero me sale este error:

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

Incluso una simple expansión de llaves como esta me da el mismo error:

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

¿Qué estoy haciendo mal?

nanangarsyad
fuente

Respuestas:

29

La sintaxis de expansión de llaves acepta comas, pero no acepta un espacio después de la coma. En muchos lenguajes de programación, los espacios después de las comas son comunes, pero no aquí. En Bash, la presencia de un espacio sin comillas evita que se realice la expansión de llaves.

Elimina el espacio y funcionará:

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

Si bien no es obligatorio, tenga en cuenta que puede mover el final .binfuera de las llaves:

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

Si desea probar el efecto de la expansión de llaves, puede usar echoo printf '%s ', o printfcon la cadena de formato que prefiera, hacer eso. (Personalmente, solo lo uso echopara esto, cuando estoy en Bash, porque la función echointegrada de Bash no expande las secuencias de escape de forma predeterminada, y por lo tanto es razonablemente adecuado para verificar qué comando realmente se ejecutará). Por ejemplo:

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
fuente
23

string{foo, bar}no es expansión de llaves; son las dos palabras string{foo,y bar}. Para usar la expansión de llaves, las llaves deben estar dentro de la misma palabra. Tendrá que eliminar el espacio extra si no lo necesita, o citarlo si lo necesita:

$ 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
fuente
+1 por el claro ejemplo, y gracias. Daré mi voto positivo cuando mi reputación sea suficiente. :)
nanangarsyad
Hay algo más que debería mencionarse sobre las "palabras de shell". . .como cómo sabe Shell dónde dividir las palabras;)
Sergiy Kolodyazhnyy
-1

Bash trata ese espacio como lo haría con cualquier otro. Como IFS, el separador de campo interno. Esto se usa para la división de palabras después de la expansión y para dividir líneas en palabras con el comando de lectura incorporado.

El shell trata a cada carácter de IFS como un delimitador y divide los resultados de las otras expansiones en palabras sobre estos caracteres. Si IFS no está establecido, o su valor es exactamente, el valor predeterminado, entonces las secuencias de, y al principio y al final de los resultados de las expansiones anteriores se ignoran, y cualquier secuencia de caracteres IFS que no esté al principio o al final sirve para delimitar palabras. Si IFS tiene un valor diferente al predeterminado, las secuencias de los espacios en blanco y la pestaña se ignoran al principio y al final de la palabra, siempre que el carácter de espacio en blanco esté en el valor de IFS (un carácter de espacio en blanco IFS). Cualquier carácter en IFS que no sea espacio en blanco IFS, junto con cualquier carácter de espacio en blanco IFS adyacente, delimita un campo. Una secuencia de caracteres de espacio en blanco IFS también se trata como un delimitador. Si el valor de IFS es nulo,
-bash (1)

Al insertar el delimitador, sin escape, le dijo a bash que su comando y argumentos son:

  1. "cp"
  2. "~ / some / dir / {my-file-to-rename.bin"
  3. "new-name-of-file.bin}"

Si hubiera tenido citas o un escape "\", tendría:

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

Que tampoco sería lo que querías, a menos que "new-name-of-file.bin" sea el nuevo nombre de archivo que deseas. Espacio incluido. A medida que la expansión de la llave se realiza primero, y luego la expansión de tilde, bash se ejecutará:

  1. "cp"
  2. "/path/to/home/some/dir/my-file-to-rename.bin"
  3. "/ ruta / a / home / some / dir / \ new-name-of-file.bin"

Simplemente eliminar el espacio solucionaría todo eso.

cde
fuente
55
Esto es principalmente bueno, pero parece decir que la división de palabras ocurre cp ~/some/dir/{my-file-to-rename.bin, new-name-of-file.bin}y IFSafecta el resultado. Tampoco es así. Aquí, el espacio es un metacarácter en la tokenización ( paso 2 ). Ver 3.5.7 sobre cuándo ocurre la división. Intenta IFS=xentonces printf '[%s]\n' {a,b} printf '[%s]\n' {a, b} printf '[%s]\n' {a,xb} printf '[%s]\n' {a, xb}.
Eliah Kagan