Copie vários arquivos via cp de uma string

1

Estou tentando copiar vários arquivos para um diretório de dentro de um shellscript. Esses arquivos contêm todos os tipos de caracteres "feios", como espaços em branco, colchetes e o que não mais. No entanto, estou preso quando se trata de escapar, pois bash e cp parecem lidar com isso de forma muito estranha.

Aqui está o cenário:
Ao emitir este comando a partir do meu shell, ele funciona como um encanto:
cp /somedir/a\ file.png /somedir/another\ file.png /someotherdir/

No entanto, ao ler os arquivos para copiar de uma string, de repente se torna estranho:
var="/somedir/a\ file.png /somedir/another\ file.png"
cp "$var" /someotherdir/

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

Acredito que isso se deva ao fato de eu dar a variável como uma string e cp acreditar 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 e cp: cannot stat 'another\': No such file or directory e cp: cannot stat 'file.png': No such file or directory

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

EDITAR : // Parece que Copiar lista de arquivos tem uma resposta com xargs , mas eu ainda me pergunto por que o bash está agindo tão estranho aqui.

    
por David Stockinger 25.02.2013 / 17:02

3 respostas

2

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

Then don’t do that!

Sério, por que você está tentando atribuir uma única variável a uma lista separada por espaço de nomes de arquivos 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 faz sentido para os comandos que você diz que usou.

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

        ︙
f1="/somedir/a file.png"
        ︙
f2="/somedir/another file.png"
        ︙
cp "$f1" "$f2" "$f3" … /someotherdir

(Você não precisa do / no final de /someotherdir/ , mas também não faz mal.) Isso é restringido pelos fatos que você precisa saber antecipadamente o número máximo de arquivos que você precisará copiar, e você precisa de alguma maneira de descobrir para qual f variável atribuir cada um.

Isso está perto do que você está fazendo agora:

var="/somedir/a\ file.png /somedir/another\ file.png …"
echo "$var" | while read f1 f2 f3 …
do
    cp "$f1" "$f2" "$f3" … /someotherdir
done

O comando read quebrará a string $var nos espaços simples, e retire as barras invertidas, deixando como apenas   (espaço). Mas você ainda precisa saber com antecedência o número máximo de arquivos que você precisará copiar. (Você precisa da construção de loop while , mesmo que esse loop seja iterado apenas uma vez.)

Talvez este seja próximo:

filelist=/tmp/my_filelist.$$
        ︙
echo "/somedir/a file.png" >> "$filelist"
        ︙
echo "/somedir/another file.png" >> "$filelist"
        ︙
while read filename
do
    cp "$filename" /someotherdir
done < "$filelist"

Outra variação é usar um array variável. Você pode aprender como fazer isso na página bash man.

    
por 26.02.2013 / 02:50
3

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

Geralmente, a melhor maneira de lidar com esse tipo de problema é armazenar os nomes de arquivos em uma matriz (um elemento por nome de arquivo) em vez de uma variável de seqüência de caracteres; então 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 criar a lista de arquivos dinamicamente, é fácil usar uma matriz:

files=() # Start with an empty list
for newfile in somethingorother; do
    files+=("$newfile")
done
    
por 26.02.2013 / 04:02
1

Se você estiver usando o bash, você pode colocar o nome do arquivo duas vezes 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/bash no 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ê deve citar duas vezes como:

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

Se você não citar duas vezes, será como usar várias variáveis separadas por espaços.

    
por 25.02.2013 / 17:39