Copiar lista de arquivos

35

Eu tenho uma lista de arquivos separados por espaços em um arquivo list.txt. Eu gostaria de copiá-los para uma nova pasta.

Eu tentei fazer:

cp `cat list.txt` new_folder

mas não funcionou.

Como você faria isso ?

Atualizar:

Obrigado por todas as suas respostas muito interessantes. Eu não pedi muito :)

Depois de ter tentado a solução acima uma segunda vez, parece que cpa ajuda de uso foi impressa porque a pasta new_folderainda não existia! Desculpe, parece ser um erro estúpido ... Quando eu crio a pasta antes, ela funciona.

No entanto, aprendi muito com todas as suas respostas e soluções, obrigado pelo tempo que você leva para escrevê-las.

Klaus
fonte
Deixe-me esclarecer uma coisa: a lista tem todos os nomes de arquivo em uma linha, separados apenas por um espaço? Ou os nomes estão em uma linha separada?
Telêmaco
Tudo em um limão, separado por um espaço. A solução de Doug funcionou :) Obrigado pela resposta.
Klaus
Isso é engraçado ... testei minha solução com uma linha na qual cada nome estava em uma linha separada.
Doug Harris
"não funcionou" não é muito significativo. Exatamente como isso não funcionou. Isso funciona bem para mim.
Pausado até novo aviso.
Desculpe: mostrou a ajuda do uso do cp. Veja minha atualização acima.
Klaus

Respostas:

34

Tente usar xargs:

cat list.txt | xargs -J % cp % new_folder

Atualizar:

Eu fiz isso no OS X, que tem uma versão diferente das versões GNU / Linux. O xargsque vem do GNU findutils não tem, -Jmas tem o -Ique é semelhante (como Dennis Williamson apontou em um comentário). A versão do xargs para OS X tem ambos -Ie -Jque têm comportamentos ligeiramente diferentes - ambos funcionarão para esta pergunta original.

$ cat list.txt
one
two.txt
three.rtf

$ cat list.txt | xargs -J % echo cp % new_folder
cp one two.txt three.rtf new_folder

$ cat list.txt | xargs -I % echo cp % new_folder
cp one new_folder
cp two.txt new_folder
cp three.rtf new_folder
Doug Harris
fonte
3
Meu xargsnão tem -Jo -Ique parece fazer a mesma coisa. Qual versão xargse qual OS / distribuição você possui?
Pausado até novo aviso.
Eu testei isso no Mac OS X 10.6 (também conhecido como snow leopard), então é a versão do xargs que acompanha o sistema operacional. Em execução xargs --versionretorna uma mensagem de erro de uso. Provavelmente é uma versão BSD do xargs. O uso de -Ie -Jsão sutilmente diferentes. Atualizarei minha resposta com exemplos bem formatados.
Doug Harris
Sim, estou trabalhando com o Mac OSX 10.6. Obrigado pelas precisões sobre -Je -I:)
Klaus
Essa é uma boa dica para usar o echo para verificar seus comandos antes de executá-los.
Liam
1
Usando o mesmo comando acima apenas com as alterações necessárias, como copio os arquivos, além de criar seus diretórios e subdiretórios, separados por barra?
Vicky Dev
54

Não é necessário cat:

xargs -a list.txt cp -t new_folder

Ou usando opções longas:

xargs --arg-file=list.txt cp --target-directory=new_folder

Aqui estão algumas versões do shell. Observe que algumas variáveis ​​não estão entre aspas ou os forloops são usados ​​especificamente porque o OP especificou que os nomes de arquivos são delimitados por espaço. Algumas dessas técnicas não funcionam para entrada de nome de arquivo por linha, na qual os nomes de arquivos contêm espaços em branco.

Bater:

for file in $(<list.txt); do cp "$file" new_folder; done

ou

cp $(<list.txt) new_folder

sh (ou Bash):

# still no cat!
while read -r line; do for file in $line; do cp "$file" new_folder; done; done < list.txt

ou

# none here either
while read -r line; do cp $line new_folder; done < list.txt

ou

# meow
for file in $(cat list.txt); do cp "$file" new_folder; done

ou

# meow
cp $(cat list.txt) new_folder
Pausado até novo aviso.
fonte
pergunta noob: por que é -tnecessário xargs -a list.txt cp -t new_folder?
Yibo Yang
1
@YiboYang: A -té uma opção de cp. Ele copia todos os argumentos de origem no diretório de destino especificado. Aqui, ele permite que o destino seja especificado antes da fonte que é adicionada pelo xargscomando.
Pausado até novo aviso.
Isso é útil, mas e se list.txt contiver o caminho misto de arquivos e pastas? Nesse caso, o comando não funciona corretamente. Ele relata "omitindo diretório ..." e se -R for especificado para o comando cp, o erro "não é um diretório" será relatado assim que o primeiro arquivo da lista for encontrado.
Bemipefe
7

Tive uma resposta embaraçosamente abrangente aqui antes, mas a resposta de Denis me lembrou que eu sentia falta da coisa mais básica. Então, eu apaguei minha resposta original. Mas como ninguém disse essa coisa muito básica, acho que vale a pena colocar aqui.

A pergunta original é "Eu tenho um arquivo de texto com uma lista de nomes de arquivos separados por espaço. Como posso copiá-los para um diretório de destino". No começo, isso pode parecer complicado ou complicado, porque você acha que precisa extrair os itens do arquivo de uma maneira específica. No entanto, quando o shell processa uma linha de comando, a primeira coisa que faz é separar a lista de argumentos em tokens e (aqui está o que ninguém disse de maneira definitiva) os espaços separados por tokens . (As novas linhas também separam os tokens, e é por isso que o teste de Doug Harris com uma lista separada de novas linhas teve o mesmo resultado.) Ou seja, o shell espera e já pode lidar com uma lista separada por espaço.

Portanto, tudo o que você precisa fazer aqui é colocar a lista separada por espaço (que você já possui) no lugar certo em seu comando. Seu comando é uma variação disso:

cp file1 file2 file3...file# target

O único problema é que você deseja obter a lista de arquivos de 1 a # do seu arquivo de texto.

Como Dennis aponta em seu comentário, sua tentativa original ( cpcat list.txt new_folder) já deveria ter funcionado. Por quê? Como o comando interno cat list.txté processado primeiro pelo shell e se expande file1 file2 file3...file#, o que é exatamente o que o shell espera e deseja nessa parte do comando. Se não funcionou, então (1) você teve um erro de digitação ou (2) seus nomes de arquivos eram de alguma forma estranhos (eles tinham espaços ou outros caracteres incomuns).

A razão pela qual todas as respostas de Dennis funcionam é simplesmente que elas fornecem a lista necessária de arquivos para cptrabalhar, colocando essa lista onde ela pertence em todo o comando. Novamente, o próprio comando é este na estrutura:

cp list-of-files target_directory

É fácil ver como tudo isso se reúne nesta versão:

cp $(<list.txt) new_folder

$()faz com que o shell execute o comando entre parênteses e substitua sua saída nesse ponto da linha maior. Em seguida, o shell executa a linha como um todo. A propósito, $()é uma versão mais moderna do que você já estava fazendo com backticks (`). Próximo: <é um operador de redirecionamento de arquivo. Diz ao shell para despejar o conteúdo da list.txtentrada padrão. Como o $()bit é processado primeiro, eis o que acontece em etapas:

  1. cp $(<list.txt) new_folder # split line into three tokens: cp, $(<list.txt), new_folder
  2. cp file1 file2 file3...file# new_folder # substitute result of $(<list.txt) into the larger command

Obviamente, o passo 2 é simplesmente o cpcomando normal que você queria.

Percebo que estou batendo muito nesse cavalo (talvez muito morto), mas acho que vale a pena. Entender exatamente como o shell processa um comando pode ajudá-lo a escrevê-lo melhor e simplificar muito. Também mostrará onde é provável que os problemas estejam escondidos. Nesse caso, por exemplo, minha primeira pergunta para você deveria ter sido sobre nomes de arquivos engraçados ou um possível erro de digitação. Não foram necessárias acrobacias.

Telêmaco
fonte
@Klaus De nada. Para ser franco, fiquei bastante irritado comigo mesmo depois de ler a resposta de Dennis e perceber que perdi completamente a parte principal do problema. A solução que você gosta é de lá também.
Telêmaco
Esta é uma resposta super antiga, mas se você precisar recriar a estrutura de pastas, precisará do --parentssinalizador. Você pode torná-lo detalhado -vpara um registro do que foi copiado. Se você deseja manter as permissões, precisa do sinalizador -aou -p. Ou seja cp --parents -av $(<list.txt) new_folder,. Isso tudo pressupõe que sua lista de arquivos seja realmente todos os arquivos; caso contrário, você também pode precisar do -rsinalizador para receber novamente.
Dhaupin 24/05
@dhaupin Na verdade, o OP afirma que ele estava no macOS. Então, pelo que vale a pena, a --parentsbandeira não existe lá. Sua versão (derivada do BSD) cpfornece apenas opções curtas, não opções longas no estilo GNU. Para um estilo BSD cp, o sinalizador para cópia recursiva é maiúsculo -R, não minúsculo.
Telemachus 31/05