Expansão de cinta bash após uma barra de caminho

12

Estou tentando copiar um arquivo para um nome diferente no mesmo diretório usando a expansão de chaves. Estou usando o bash 4.4.18.

Aqui está o que eu fiz:

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

mas eu recebo este erro:

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

Mesmo uma simples expansão de chave como essa me dá o mesmo erro:

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

O que estou fazendo de errado?

nanangarsyad
fonte

Respostas:

29

A sintaxe de expansão do colchete aceita vírgulas, mas não aceita um espaço após a vírgula. Em muitas linguagens de programação, espaços após vírgulas são comuns, mas não aqui. No Bash, a presença de um espaço sem aspas impede a execução da expansão de chaves.

Remova o espaço e ele funcionará:

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

Embora não seja de todo necessário, observe que você pode mover a trilha para .binfora dos aparelhos:

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

Se você quiser testar o efeito da expansão entre chaves, poderá usar isso echoou printf '%s ', ou printfcom a sequência de formatos que preferir. (Pessoalmente, eu apenas uso echoisso quando estou no Bash, porque o echobuilt-in do Bash não expande as seqüências de escape por padrão e, portanto, é razoavelmente adequado para verificar qual comando realmente será executado.) Por exemplo:

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

string{foo, bar}não é expansão de chaves; são as duas palavras string{foo,e bar}. Para usar a expansão de chaves, as chaves precisam estar dentro da mesma palavra. Você precisará remover o espaço extra, se não precisar, ou citá-lo, se precisar:

$ 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
fonte
+1 para o exemplo claro e obrigado. Eu darei meu voto positivo quando minha reputação for suficiente. :)
nanangarsyad
Há algo mais que deve ser mencionado sobre "palavras de casca". . .como como sabe shell onde palavras de divisão;)
Sergiy Kolodyazhnyy
-1

Bash trata esse espaço como qualquer outro. Como IFS, o separador de campo interno. Isso é usado para dividir palavras após a expansão e para dividir linhas em palavras com o comando read builtin.

O shell trata cada caractere do IFS como um delimitador e divide os resultados das outras expansões em palavras nesses caracteres. Se o IFS estiver desativado ou seu valor for exatamente, o padrão, as seqüências de, e no início e no final dos resultados das expansões anteriores serão ignoradas e qualquer sequência de caracteres do IFS que não esteja no início ou no final serve para delimitar palavras. Se o IFS tiver um valor diferente do padrão, as seqüências dos caracteres de espaço em branco e da guia serão ignoradas no início e no final da palavra, desde que o caractere de espaço em branco esteja no valor do IFS (um caractere de espaço em branco do IFS). Qualquer caractere no IFS que não seja espaço em branco do IFS, juntamente com qualquer caractere de espaço em branco do IFS adjacente, delimita um campo. Uma sequência de caracteres de espaço em branco do IFS também é tratada como um delimitador. Se o valor do IFS for nulo,
-bash (1)

Ao inserir o delimitador, sem escape, você disse ao bash que seu comando e argumentos são:

  1. "cp"
  2. "~ / some / dir / {meu-arquivo-para-renomear.bin",
  3. "novo nome do arquivo.bin}"

Se você tivesse aspas ou uma fuga "\", teria:

  1. "cp"
  2. "~ / some / dir / {meu-arquivo-para-renomear.bin, \ novo-nome-do-arquivo.bin}"

O que também não seria o que você queria, a menos que "new-name-of-file.bin" seja o novo nome de arquivo desejado. Espaço incluído. À medida que a expansão do braquete acontece primeiro, e depois a expansão do til, o bash executa:

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

Simplesmente remover o espaço consertaria tudo isso.

cde
fonte
5
Isso é bom, mas você parece dizer que a divisão de palavras acontece cp ~/some/dir/{my-file-to-rename.bin, new-name-of-file.bin}e IFSafeta o resultado. Nem é assim. Aqui, o espaço é um metacaractere na tokenização ( etapa 2 ). Veja 3.5.7 quando a divisão acontece. Tente IFS=xentão printf '[%s]\n' {a,b} printf '[%s]\n' {a, b} printf '[%s]\n' {a,xb} printf '[%s]\n' {a, xb}.
Elias Kagan