Copie vários arquivos via cp de uma string

Copie vários arquivos via cp de uma string

Estou tentando copiar vários arquivos para um diretório de um shellscript. Esses arquivos contêm todos os tipos de caracteres "feios", como espaços em branco, colchetes e outros. No entanto, estou preso quando se trata de escapar disso bashe cppareço lidar com isso de maneira muito estranha.

Aqui está o cenário:
Ao emitir este comando de dentro do meu shell, ele funciona perfeitamente:
cp /somedir/a\ file.png /somedir/another\ file.png /someotherdir/

Porém, ao ler os arquivos para copiar de uma string, de repente fica estranho: resulta em
var="/somedir/a\ file.png /somedir/another\ file.png"
cp "$var" /someotherdir/

cp: cannot stat 'a\\ file.png another\\ file.png': No such file or directory

Acredito que isso se deva ao fato de eu fornecer a variável como uma string e cpacreditar que é um arquivo, embora sejam vários arquivos. Ao emitir o mesmo comando sem colocar a variável entre aspas ( var="/somedir/a\ file.png /somedir/another\ file.png"; cp $var /someotherdir/), recebo um erro ainda mais estranho:
cp: cannot stat 'a\\ file.png another\\ file.png': No such file or directory
cp: cannot stat 'file.png': No such file or directory
cp: cannot stat 'another\\': No such file or directory
cp: cannot stat 'file.png': No such file or directory

Parece ignorar completamente minha fuga. O que estou fazendo de errado?

EDITAR:// Parece queCopiar lista de arquivostem uma resposta com xargs, mas ainda me pergunto por que o bash está agindo de forma tão estranha aqui.

Responder1

Doutor, dói quando eu faço isso...

Então não faça isso!

Sério, por que você está tentando atribuir a uma única variável uma lista de nomes de arquivos separados por espaços que contêm espaços? Essa é uma receita para o desastre.

Para responder à sua pergunta implícita: concordo que as mensagens de erro que você mostra não fazem sentido para os comandos que você diz ter usado.

Como você não disse como determinar quais arquivos copiar, é difícil saber qual solução funcionará para você e qual não funcionará. Aqui está uma abordagem possível:


f1="/somedir/a arquivo.png"
f2="/somedir/outro arquivo.png"
cp "$f1" "$f2" "$f3"… /algumoutrodir

(Você não precisa do /no final de /someotherdir/, mas também não faz mal.) Isso é limitado pelos fatos de que você precisa saber com antecedência o número máximo de arquivos que precisará copiar e precisa de alguns maneira de descobrir a qual fvariável atribuir cada uma.

Isso está próximo do que você está fazendo agora:

var="/somedir/a\ arquivo.png /somedir/another\ arquivo.png …"
eco "$var" | enquanto lê f1 f2 f3…
fazer
    cp "$f1" "$f2" "$f3"… /algumoutrodir
feito

O readcomando irá separar a $varstring nos espaços simples e retirar as barras invertidas, deixando apenas  (espaço). Mas você ainda precisa saber com antecedência o número máximo de arquivos que precisará copiar. (Você precisa da whileconstrução do loop, mesmo que esse loop seja iterado apenas uma vez.)

Talvez este esteja chegando perto:

lista de arquivos=/tmp/minha_lista de arquivos.$$
echo "/somedir/a arquivo.png" >> "$filelist"
echo "/somedir/outro arquivo.png" >> "$filelist"
enquanto lê o nome do arquivo
fazer
    cp "$nome do arquivo" /algumoutrodiretório
concluído < "$ lista de arquivos"

Outra variação é usar um array variável. Você pode aprender como fazer isso na bashpágina de manual.

Responder2

Quando o bash analisa uma linha de comando, ele primeiro interpreta aspas, escapes e outros itens e depois substitui as variáveis. Se houver escapes, aspas, etc. nos valores das variáveis, eles nunca terão efeito porque, no momento em que forem incluídos no comando, será tarde demais para que tenham o efeito pretendido.

Geralmente, a melhor maneira de lidar com esse tipo de problema é armazenar os nomes dos arquivos em um array (um elemento por nome de arquivo) em vez de uma variável de string; em seguida, use "${array[@]}"para expandir a matriz com cada elemento sendo tratado como uma "palavra" separada:

files=(/somedir/a\ file.png /somedir/another\ file.png)
cp "${files[@]}" /someotherdir/

Atualização: se você precisar construir a lista de arquivos dinamicamente, isso é fácil com um array:

files=() # Start with an empty list
for newfile in somethingorother; do
    files+=("$newfile")
done

Responder3

Se estiver usando o bash, você pode colocar aspas duplas no nome do arquivo para evitar o uso de caracteres de escape:

cp /old_location/This\ is\ an\ \[ugly\] \file.tmp /new_location/This\ is\ an\ \[ugly\] \file.tmp

use isso ao usar #!/bin/bashem seu shellscript:

cp "/old_location/This is an [ugly] file with extras $$$.tmp" "/new_location/Ugly file with extras $$$.tmp"

No seu caso, você está usando os valores dentro de uma variável, mas está passando a variável errada. Se uma variável tiver espaços, você também deve aspas duplas:

var="/somedir/a\ file.png /somedir/another\ file.png"; cp "$var" /someotherdir/

Se você não colocar aspas duplas, será como usar várias variáveis ​​separadas por espaços.

informação relacionada