Expansão da cinta Bash após uma barra de caminho

10

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

Veja o que eu fiz:

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

mas recebo este erro:

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

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

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

O que estou fazendo de errado?

    
por nanangarsyad 05.04.2018 / 00:19

3 respostas

28

A sintaxe expansão de contraventores aceita vírgulas, mas não aceita 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 que a expansão do suporte seja executada.

Remova o espaço e funcionará:

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

Embora não seja obrigatório, observe que você pode mover o .bin à direita para fora das chaves:

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

Se você quiser testar o efeito da expansão de chaves, use echo ou printf '%s ' ou printf com qualquer string de formato que você preferir, para fazer isso. (Pessoalmente eu uso apenas echo para isso, quando estou no Bash, porque Bash echo builtin não expande seqüências de escape por padrão, e é razoavelmente adequado para verificar qual comando 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
    
por 05.04.2018 / 00:25
22

string{foo, bar} não é a expansão de chaves; são as duas palavras string{foo, e bar} . Para usar a expansão de contraventamento, as chaves precisam estar dentro da mesma palavra. Você terá que 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
    
por 05.04.2018 / 00:27
-1

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

The shell treats each character of IFS as a delimiter, and splits the results of the other expansions into words on these characters. If IFS is unset, or its value is exactly , the default, then sequences of , , and at the beginning and end of the results of the previous expansions are ignored, and any sequence of IFS characters not at the beginning or end serves to delimit words. If IFS has a value other than the default, then sequences of the whitespace characters space and tab are ignored at the beginning and end of the word, as long as the whitespace character is in the value of IFS (an IFS whitespace character). Any character in IFS that is not IFS whitespace, along with any adjacent IFS whitespace characters, delimits a field. A sequence of IFS whitespace characters is also treated as a delimiter. If the value of IFS is null, no word splitting occurs.
-bash(1)

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

  1. "cp"
  2. "~ / algum / dir / {meu-ficheiro-para-renomear.bin,"
  3. "new-name-of-file.bin" "

Se você tivesse citações ou um escape "\", teria:

  1. "cp"
  2. "~ / algum / dir / {meu-arquivo-para-renomear.bin, \ novo-nome-de-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 que você queria. Espaço incluído. Como a expansão da chave acontece primeiro e depois a expansão do til, o bash seria executado:

  1. "cp"
  2. "/ caminho / para / home / algum / dir / meu-arquivo-para-renomear.bin"
  3. "/ caminho / para / home / algum / dir / \ novo-nome-de-arquivo.bin"

Basta remover o espaço para corrigir tudo isso.

    
por 05.04.2018 / 04:59